﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
using UMC.Data;
using UMC.Net;
using UMC.Security;
using UMC.Web;
using System.IO;
using System.Collections;
using UMC.ITME.Entities;
using System.Net;
using System.Text.RegularExpressions;
using System.Buffers;
using NSec.Cryptography;
using System.Net.Sockets;
using UMC.Bridge;
using System.Threading;


namespace UMC.ITME
{
    public class WebServlet : UMC.Web.WebServlet
    {
        internal protected virtual bool CheckUser(StringValue configKey, StringValue userName, StringValue domain)
        {
            return true;
        }
        static WebServlet()
        {
            HighFrequency.Put(new SiteConfig.PageConfig<int> { EndPath = StringValue.Empty, StartPath = new StringValue("/Proxy/") });
            HighFrequency.Put(new SiteConfig.PageConfig<int> { EndPath = StringValue.Empty, StartPath = new StringValue("/UMC.WS/") });
            HighFrequency.Put(new SiteConfig.PageConfig<int> { EndPath = StringValue.Empty, StartPath = new StringValue("/Dns/") });
        }
        static NSec.Cryptography.Key _Ed25519, _X25519;
        public static NSec.Cryptography.Key X25519
        {
            get
            {
                if (_X25519 == null)
                {
                    Span<byte> scurve25519 = stackalloc byte[32];
                    var privateKey = Ed25519.Export(NSec.Cryptography.KeyBlobFormat.RawPrivateKey);
                    Utility.ToCurveSecretKey(privateKey, scurve25519);
                    _X25519 = NSec.Cryptography.Key.Import(NSec.Cryptography.KeyAgreementAlgorithm.X25519, scurve25519, KeyBlobFormat.RawPrivateKey);//new KeyCreationParameters { ExportPolicy = KeyExportPolicies.AllowPlaintextExport });

                }
                return _X25519;
            }
        }
        static StringValue _AppId = StringValue.Empty, _appIdKey = StringValue.Empty;
        public static StringValue AppId
        {
            get
            {
                if (_AppId.IsEmpty)
                {
                    var appId = WebResource.Instance().Provider["appId"];
                    if (String.IsNullOrEmpty(appId) == false)
                    {
                        _AppId = new StringValue(Utility.Guid(appId).Value.ToByteArray());
                    }
                }
                return _AppId;
            }
        }
        public static StringValue AppIdKey
        {
            get
            {
                if (_appIdKey.IsEmpty)
                {
                    var appId = WebResource.Instance().Provider["appId"];
                    if (String.IsNullOrEmpty(appId) == false)
                    {
                        _appIdKey = new StringValue(Utility.Parse36Encode(Utility.IntParse(Utility.Guid(appId).Value)));
                    }
                }
                return _appIdKey;
            }
        }

        static StringValue _appSecret = StringValue.Empty;
        public static StringValue AppSecret
        {
            get
            {
                if (_appSecret.IsEmpty)
                {
                    var appId = WebResource.Instance().Provider["appSecret"];
                    if (String.IsNullOrEmpty(appId) == false)
                    {
                        _appSecret = new StringValue(Utility.Guid(appId).Value.ToByteArray());
                    }
                }
                return _appSecret;
            }
        }

        public static NSec.Cryptography.Key Ed25519
        {
            get
            {

                if (_Ed25519 == null)
                {
                    var pconfig = UMC.Data.DataFactory.Instance().Config("SITE_Ed25519".ToUpper())?.ConfValue ?? StringValue.Empty;
                    if (pconfig.Length == 32)
                    {
                        _Ed25519 = NSec.Cryptography.Key.Import(NSec.Cryptography.SignatureAlgorithm.Ed25519, pconfig.Span, NSec.Cryptography.KeyBlobFormat.RawPrivateKey);

                    }
                }
                if (_Ed25519 == null)
                {
                    _Ed25519 = NSec.Cryptography.Key.Create(NSec.Cryptography.SignatureAlgorithm.Ed25519, new KeyCreationParameters { ExportPolicy = KeyExportPolicies.AllowPlaintextExport });
                    var privateKey = _Ed25519.Export(NSec.Cryptography.KeyBlobFormat.RawPrivateKey);
                    UMC.Data.DataFactory.Instance().Put(new UMC.Data.Entities.Config { ConfKey = "SITE_Ed25519".ToUpper(), ConfValue = new StringValue(privateKey) });
                }
                return _Ed25519;
            }
        }
        void Unauthorized(Net.NetContext context)
        {
            var callback = context.QueryString.Get("oauth_callback");

            if (String.IsNullOrEmpty(callback))
            {
                context.StatusCode = 403;
                var reDomain = AuthDomain(context);
                String loginUrl;
                if (String.Equals(context.Host.UTF8(), reDomain.Host) == false)
                {
                    loginUrl = new Uri(reDomain, $"/Unauthorized?transfer={GetCookie(context)}&oauth_callback={Uri.EscapeDataString(context.Url.AbsoluteUri)}").AbsoluteUri;

                }
                else
                {
                    loginUrl = new Uri(reDomain, "/Unauthorized?oauth_callback=/").AbsoluteUri;
                }
                context.AddHeader("Refresh", $"0;URL={loginUrl}");
                var title = AppConf["title"] ?? "ITME";
                WebServlet.HtmlResource(context, "loading", $"<div class=\"app-loading-title\">{title}</div></div></div><script>location.href='{loginUrl}'</script></body></html>");

            }
            else
            {
                Unauthorized(context, callback);
            }
        }
        void Close(Net.NetContext context)
        {
            context.StatusCode = 403;

            HtmlResource(context, "close");
        }
        internal static void HtmlResource(Net.NetContext context, string key, String html = "")
        {
            context.ContentType = StringValue.ContentTypeHtml;// "text/html";
            using (System.IO.Stream stream = typeof(WebServlet).Assembly
                               .GetManifestResourceStream($"UMC.ITME.Resources.{key}.html"))
            {

                var appId = UMC.Data.WebResource.Instance().Provider["appId"];
                var sb = new System.Text.StringBuilder();
                sb.Append(html);
                sb.AppendLine();
                sb.Append("<!--Version:");
                sb.Append(APIProxy.Version);
                if (!String.IsNullOrEmpty(appId))
                {
                    sb.Append("|");
                    sb.Append(appId);
                }
                sb.AppendLine("-->");
                var bytes = System.Text.Encoding.UTF8.GetBytes(sb.ToString());
                context.ContentLength = stream.Length + bytes.Length;
                stream.CopyTo(context.OutputStream);
                context.OutputStream.Write(bytes, 0, bytes.Length);
            }
        }
        void NotSupport(Net.NetContext context)
        {
            context.StatusCode = 401;
            HtmlResource(context, "Auth.nosupport");
        }
        void WebSSH(Net.NetContext context, String path)
        {
            var paths = new List<String>(path.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));

            var dv = context.QueryString.Get("d");
            var t = context.QueryString.Get("t");
            if (String.IsNullOrEmpty(dv) == false && String.IsNullOrEmpty(t) == false)
            {
                try
                {
                    var d = Utility.DES(Utility.FromBase64String(dv), Utility.Guid(Web.WebActivity.TempKey).Value);
                    var secretKey = Utility.MD5(d, Web.WebActivity.TempKey);
                    var totp = new Totp(secretKey, 30, OtpHashMode.Sha1, 6);
                    if (totp.VerifyTotp(t, out var _, new VerificationWindow(1, 1)))
                    {
                        context.Cookies[SessionCookieName] = new StringValue(d);
                    }
                }
                catch
                {

                }

            }
            context.Token = this.AccessToken(context);

            if (context.Token.IsInRole(UMC.Security.Membership.UserRole))
            {
                if (paths.Count > 0)
                {
                    if (Security.AuthManager.Authorization(context.Token.Identity(), 0, new StringValue($"Device/{paths[0]}"), out var isBiometric) > 0)
                    {
                        if (isBiometric && context.Token.BiometricTime == 0)
                        {
                            var seesionKey = UMC.Data.Utility.Guid(context.Token.Device.Value);
                            var url = $"/Biometric?oauth_callback={Uri.EscapeDataString(context.Url.AbsoluteUri)}&transfer={seesionKey}";
                            context.Redirect(url);
                        }
                        else
                        {
                            if (path.EndsWith('/'))
                            {
                                HtmlResource(context, "desktop.ssh");
                            }
                            else
                            {
                                var device = UMC.Data.HotCache.Get(new Entities.Device { Id = Utility.UInt64Parse(paths[0], 0) });
                                if (device != null && (device.Type ?? DeviceType.Tcp) == DeviceType.Ssh)
                                {
                                    paths.RemoveAt(0);
                                    var password = UMC.Data.DataFactory.Instance().Password(SiteConfig.MD5Key(device.Id.ToString(), device.Username));

                                    try
                                    {

                                        using (var ssh = new UMC.SshNet.SftpClient(device.Ip, device.Port ?? 22, device.Username, password))
                                        {
                                            ssh.Connect();
                                            if (ssh.Get("/" + String.Join("/", paths)).IsDirectory)
                                            {
                                                HtmlResource(context, "desktop.ssh");
                                            }
                                            else
                                            {
                                                context.ContentType = StringValue.ContentTypeStream;// "application/octet-stream";
                                                context.AddHeader(StringValue.ContentDisposition, $"attachment;filename={Uri.EscapeDataString(paths[paths.Count - 1])}");

                                                using (var stream = ssh.OpenRead("/" + String.Join("/", paths)))
                                                {

                                                    var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(1024);
                                                    try
                                                    {

                                                        int l = 0;
                                                        while ((l = stream.Read(buffer, 0, buffer.Length)) > 0)
                                                        {
                                                            context.OutputStream.Write(buffer, 0, l);
                                                        }
                                                    }
                                                    finally
                                                    {

                                                        System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
                                                    }

                                                }
                                            }

                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        Error(context, "下载失败", ex.Message);

                                    }
                                }
                                else
                                {
                                    Error(context, "ITME Web SSH", "没有此设备资源，请联系管理员");
                                }
                            }
                        }

                    }
                    else
                    {
                        Error(context, "ITME Web SSH", "此Web SSH未授权，请联系管理员");

                    }

                }
                else
                {
                    HtmlResource(context, "desktop.ssh");

                }
            }
            else
            {
                context.Redirect($"/Unauthorized?oauth_callback={Uri.EscapeDataString(context.Url.PathAndQuery)}");
            }

        }

        void Auth(Net.NetContext context)
        {
            if (context.Method == HttpMethod.POST)//.Span.SequenceEqual(Mime.POST))
            {
                context.UseSynchronousIO();
                context.ReadAsForm(ns =>
                {
                    var sign = ns["umc-request-sign"];
                    var appName = ns["umc-request-app"];
                    ns.Remove("umc-request-sign"u8);
                    try
                    {
                        if (String.IsNullOrEmpty(sign))
                        {
                            context.StatusCode = 403;

                            UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", "缺少umc-request-sign参数"), context.Output);
                            return;
                        }
                        if (String.IsNullOrEmpty(appName))
                        {
                            context.StatusCode = 403;

                            UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", "缺少umc-request-app参数"), context.Output);
                            return;

                        }
                        var site = DataFactory.Instance().Site(appName);
                        if (site == null)
                        {
                            context.StatusCode = 403;

                            UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", $"未找到{appName}应用"), context.Output);
                            return;

                        }
                        if (String.IsNullOrEmpty(site.AppSecret))
                        {
                            context.StatusCode = 403;

                            UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", $"{site.Caption}未设置AppSecret"), context.Output);
                            return;

                        }
                        if (String.Equals(Utility.Sign(ns, ReadOnlySpan<byte>.Empty, site.AppSecret), sign, StringComparison.CurrentCultureIgnoreCase))
                        {
                            var time = UMC.Data.Utility.IntParse(ns["umc-request-time"], 0);
                            if (Math.Abs(UMC.Data.Utility.TimeSpan() - time) > 300)
                            {
                                context.StatusCode = 403;
                                UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", "umc-request-time 参数不符合规定"), context.Output);
                            }
                            else
                            {
                                var auth_code = ns[new StringValue("umc-request-code")];
                                if (auth_code.IsEmpty)
                                {
                                    if (((site.AppSecretAuth ?? 0) & 2) == 0)
                                    {
                                        context.StatusCode = 403;
                                        UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", $"{site.Caption}未开通模拟登录"), context.Output);
                                        return;
                                    }
                                    var name = ns["umc-request-user-name"];
                                    if (String.IsNullOrEmpty(name))
                                    {
                                        context.StatusCode = 403;
                                        UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", "缺少umc-request-user-name 参数"), context.Output);
                                        return;
                                    }
                                    var id = ns["umc-request-user-id"];
                                    var srole = ns["umc-request-user-role"];
                                    var alias = ns["umc-request-user-alias"];
                                    var sid = new Guid(UMC.Data.Utility.MD5("umc.api.auth", id, name, srole, alias, appName));
                                    var user = UMC.Security.Membership.Instance().Identity(name);
                                    if (user == null)
                                    {
                                        String[] roles = new string[0];
                                        if (String.IsNullOrEmpty(srole) == false)
                                        {
                                            roles = srole.Split(',').Where(r => String.Equals(r, UMC.Security.AccessToken.AdminRole) == false).ToArray();

                                        }
                                        var uid = Data.Utility.Guid(id) ?? Utility.Guid(name, true).Value;
                                        user = UMC.Security.Identity.Create(uid, name, alias ?? name, roles);
                                    }

                                    new Session<UMC.Security.AccessToken>(new UMC.Data.AccessToken(sid).Login(user, 30 * 60), UMC.Data.Utility.Guid(sid))
                                        .Commit(user.Id.Value, "umc.api.auth", false, $"{context.UserHostAddress}/{context.Server}");


                                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "success").Put("data", new WebMeta("device", UMC.Data.Utility.Guid(sid))), context.Output);
                                }
                                else
                                {
                                    var seesion = UMC.Data.DataFactory.Instance().Session(auth_code);
                                    if (seesion != null)
                                    {
                                        var Value = UMC.Data.JSON.Deserialize<UMC.Data.AccessToken>(seesion.Content.Span);
                                        var user = Value.Identity();
                                        UMC.Data.DataFactory.Instance().Delete(seesion);
                                        var webMeta = new WebMeta();
                                        webMeta.Put("name", user.Name).Put("id", user.Id).Put("alias", user.Alias).Put("roles", user.Roles).Put("organizes", user.Organizes);
                                        UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("code", "success").Put("data", webMeta), context.Output);
                                    }
                                    else
                                    {
                                        context.StatusCode = 403;
                                        UMC.Data.JSON.Serialize(new WebMeta("code", "error").Put("msg", "code仅能使用一次"), context.Output);
                                    }
                                }
                            }
                        }
                        else
                        {
                            context.StatusCode = 403;
                            UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "签名不正确"), context.Output);
                        }
                    }
                    finally
                    {
                        context.OutputFinish();
                    }
                });

            }
            else
            {
                var appid = context.QueryString.Get("appid");
                var redirect_uri = context.QueryString.Get("redirect_uri");

                if (String.IsNullOrEmpty(appid))
                {
                    context.StatusCode = 403;
                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "缺少appid参数"), context.Output);
                    return;
                }

                var site = DataFactory.Instance().Site(appid);
                if (site == null)
                {
                    context.StatusCode = 403;
                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "不存在此App"), context.Output);
                    return;
                }
                if (String.IsNullOrEmpty(site.AppSecret))
                {
                    context.StatusCode = 403;
                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", $"{site.Caption}未设置AppSecret"), context.Output);
                    return;

                }
                if (((site.AppSecretAuth ?? 0) & 1) == 0)
                {
                    context.StatusCode = 403;
                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta("code", "error").Put("msg", $"{site.Caption}求未开通OAuth授权"), context.Output);
                    return;
                }
                if (String.IsNullOrEmpty(redirect_uri))
                {
                    context.StatusCode = 403;
                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "缺少redirect_uri参数"), context.Output);
                    return;
                }

                context.Token = this.AccessToken(context);
                if (context.Token.IsAuthenticated)
                {
                    var seesionKey = Utility.MD5(context.Token.Device.Value);

                    var sesion = UMC.Data.DataFactory.Instance().Session(new StringValue(context.Token.Device.ToString()));

                    if (sesion != null)
                    {
                        sesion.SessionKey = new StringValue(seesionKey);
                        UMC.Data.DataFactory.Instance().Put(sesion);
                        if (redirect_uri.Contains("?"))
                        {
                            context.Redirect($"{redirect_uri}&auth_code={seesionKey}");
                        }
                        else
                        {
                            context.Redirect($"{redirect_uri}?auth_code={seesionKey}");
                        }

                    }
                    else
                    {
                        context.StatusCode = 403;
                        UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "服务端错误，请联系管理员"), context.Output);

                    }
                }
                else
                {
                    Unauthorized(context);
                }

            }

        }
        public static StringValue AuthPre
        {
            get; set;
        } = StringValue.Empty;

        internal static Uri AuthDomain(Net.NetContext context)
        {
            var Domain = MainDomain;

            if (String.Equals("localhost", MainDomain) == false)
            {
                if (AuthPre.IsEmpty)
                {
                    return new Uri($"{context.Url.Scheme}://{Domain}");
                }
                else
                {
                    return new Uri($"{context.Url.Scheme}://{AuthPre}.{Domain}");
                }
            }
            else
            {
                return new Uri(context.Url, "/");
            }
        }
        protected override UMC.Security.AccessToken AccessToken(NetContext context)
        {

            var sessionKeys = context.Cookies.GetValues(SessionCookieName.Span);
            switch (sessionKeys?.Length ?? 0)
            {
                case 0:
                    {
                        bool isAutoCookie = context.Method != HttpMethod.OPTIONS;
                        if (isAutoCookie && context.Headers.TryGet(StringValue.SecFetchMode.Span, out var mode))
                        {
                            if (mode.Value.Span.SequenceEqual("cors"u8))
                            {
                                isAutoCookie = false;
                            }
                        }
                        var deviceId = Data.Utility.Guid(NewCookie(context, isAutoCookie), true).Value;

                        return new UMC.Data.AccessToken(deviceId).Login(new UMC.Security.Guest(deviceId), 0);
                    }
                case 1:
                    {
                        if (sessionKeys[0].IsEmpty)
                        {
                            goto case 0;
                        }


                        Guid deviceId = Data.Utility.Guid(sessionKeys[0].Span).Value;

                        var session = new Session<Data.AccessToken>(deviceId.ToString());
                        if (session.Value?.Device == deviceId)
                        {
                            var auth = session.Value;

                            var passDate = Data.Utility.TimeSpan();
                            if (auth.Timeout > 0)
                            {
                                if (((auth.ActiveTime ?? 0) + auth.Timeout) < passDate)
                                {
                                    auth.SignOut().Commit(context.UserHostAddress, context.Server);
                                    return auth;

                                }
                            }
                            if (auth.BiometricTime > 0)
                            {
                                if (session.ClientIP.Span.StartsWith(context.UserHostAddress.UTF8()))
                                {
                                    if (auth.ActiveTime < passDate - 300)
                                    {
                                        if (auth.ActiveTime < passDate - 600)
                                        {
                                            auth.BiometricTime = 0;
                                        }
                                        auth.Commit(context.UserHostAddress, context.Server);
                                    }
                                }
                                else
                                {
                                    auth.BiometricTime = 0;

                                    if (auth.ActiveTime < passDate - 600)
                                    {
                                        auth.Commit(context.UserHostAddress, context.Server);
                                    }
                                }
                            }
                            else if (auth.ActiveTime < passDate - 600)
                            {
                                auth.Commit(context.UserHostAddress, context.Server);
                            }

                            context.Token = auth;
                            return auth;
                        }

                        return new UMC.Data.AccessToken(deviceId).Login(new UMC.Security.Guest(deviceId), 0);  // Security.AccessToken.Create<Data.AccessToken>(new UMC.Security.Guest(deviceId), deviceId, 0); ;
                    }
                // break;
                default:
                    var sessionKey = sessionKeys[0];// Utility.Guid(Guid.NewGuid());
                    context.Cookies[SessionCookieName] = sessionKey;//context.Memory.Rent(sessionKey);

                    var cdmn = MainDomain;
                    var cdm = cdmn;
                    if (DomainUnion == '-')
                    {
                        cdm = cdmn.Substring(cdmn.IndexOf('.') + 1);
                    }
                    if (context.Url.Host.EndsWith(cdmn))
                    {
                        var hs = context.Url.Host.Split('.');
                        for (int i = 0; i < hs.Length - 1; i++)
                        {
                            var h = String.Join('.', hs.AsSpan(i, hs.Length - i).ToArray());

                            if (h == cdm)
                            {
                                context.AddHeader(StringValue.SetCookie, $"{SessionCookieName}={sessionKey}; SameSite=None; Secure; Expires={DateTime.Now.AddYears(10):r}; HttpOnly; Domain={cdm}; Path=/");

                            }
                            else if (i == 0)
                            {
                                context.AddHeader(StringValue.SetCookie, $"{SessionCookieName}={sessionKey}; SameSite=None; Secure; Expires={DateTime.Now.AddDays(-100):r}; HttpOnly; Path=/");

                                context.AddHeader(StringValue.SetCookie, $"{SessionCookieName}={sessionKey}; SameSite=None; Secure; Expires={DateTime.Now.AddDays(-100):r}; HttpOnly; Domain={h}; Path=/");
                            }
                            else
                            {
                                context.AddHeader(StringValue.SetCookie, $"{SessionCookieName}={sessionKey}; SameSite=None; Secure; Expires={DateTime.Now.AddDays(-100):r}; HttpOnly; Domain={h}; Path=/");
                            }
                        }



                    }

                    goto case 1;

            }
        }

        void Unauthorized(Net.NetContext context, string oauth_callback)
        {
            context.NotCounted();
            context.Token = this.AccessToken(context);

            var reDomain = AuthDomain(context);

            var transfer = context.QueryString.Get("transfer");
            if (context.Token.IsAuthenticated)
            {
                if (String.IsNullOrEmpty(transfer) == false && String.Equals(GetCookie(context), transfer) == false)
                {
                    UMC.Data.DataFactory.Instance().Delete(new Data.Entities.Session { SessionKey = new StringValue(transfer) });
                    var login = UMC.Data.Reflection.Configuration("account")?["login"];
                    var timeout = UMC.Data.Utility.IntParse(login?.Attributes?["timeout"], 3600);
                    new UMC.Data.AccessToken(Utility.Guid(transfer, true).Value).Login(context.Token.Identity(), timeout).Commit("Desktop", context.UserHostAddress, context.Server);
                }
                var authUrl = new Uri(context.Url, oauth_callback);
                if (authUrl.Host == context.Url.Host)
                {
                    if (context.Token.IsInRole(UMC.Security.Membership.UserRole))
                    {
                        context.Redirect(oauth_callback);
                        return;
                    }
                }
                else
                {
                    context.Redirect(oauth_callback);
                    return;

                }
            }
            var webr = UMC.Data.WebResource.Instance();


            var ua = context.UserAgent;
            if (ua.IsEmpty == false)
            {
                if (ua.Contains("UMC CLIENT"u8, true))
                {
                    context.StatusCode = 401;
                    context.AddHeader(StringValue.CacheControl, "no-store");
                    context.ContentType = StringValue.ContentTypeHtml;// "text/html; charset=UTF-8";
                    using (System.IO.Stream stream = typeof(WebServlet).Assembly
                                             .GetManifestResourceStream("UMC.ITME.Resources.umc.html"))
                    {
                        context.ContentLength = stream.Length;
                        stream.CopyTo(context.OutputStream);

                    }

                }
                else if (ua.Contains("MICROMESSENGER"u8, true) || ua.Contains("WXWORK"u8, true))
                {
                    var account = UMC.Data.Reflection.Configuration("account");
                    var appids = new List<String>();
                    var wkappids = new List<String>();
                    foreach (var p in account.Providers)
                    {

                        switch (p.Type)
                        {
                            case "weixin":
                                appids.Add(p.Name);
                                break;
                            case "wxwork":
                                wkappids.Add(p.Name);
                                break;
                        }
                    }
                    if (wkappids.Count > 0)
                    {
                        context.Token.Put("oauth_callback", oauth_callback).Commit(context.UserHostAddress, context.Server);
                        var wxP = account[wkappids[0]];
                        var loginUrl = wxP["loginUrl"];
                        if (String.IsNullOrEmpty(loginUrl) == false)
                        {
                            context.Redirect(new Uri(loginUrl.Replace("{device}", $"AUTO_{transfer}")).AbsoluteUri);
                        }
                        else
                        {
                            var agentid = wxP["agentid"];
                            var redirect = wxP["redirect"];

                            var redirect_uri = Uri.EscapeDataString(String.IsNullOrEmpty(redirect) ? new Uri(reDomain, $"/UMC/wxwork.html?appid={wxP.Name}").AbsoluteUri : redirect);
                            var urlStr = $"https://open.weixin.qq.com/connect/oauth2/authorize?appid={wxP.Name}&response_type=code&scope=snsapi_base&state=AUTO_{transfer}&redirect_uri={redirect_uri}#wechat_redirect";

                            if (String.IsNullOrEmpty(agentid) == false)
                            {
                                urlStr = $"https://open.weixin.qq.com/connect/oauth2/authorize?appid={wxP.Name}&response_type=code&agentid={agentid}&scope=snsapi_privateinfo&state=AUTO_{transfer}&redirect_uri={redirect_uri}#wechat_redirect";

                            }
                            context.Redirect(urlStr);
                        }
                    }
                    else if (appids.Count > 0)
                    {
                        context.Token.Put("oauth_callback", oauth_callback).Commit(context.UserHostAddress, context.Server);

                        var wxP = account[wkappids[0]];
                        var loginUrl = wxP["loginUrl"];
                        if (String.IsNullOrEmpty(loginUrl) == false)
                        {
                            context.Redirect(new Uri(loginUrl.Replace("{device}", $"AUTO_{transfer}")).AbsoluteUri);
                        }
                        else
                        {
                            var redirect = wxP["redirect"];

                            var redirect_uri = Uri.EscapeDataString(String.IsNullOrEmpty(redirect) ? new Uri(reDomain, $"/UMC/weixin.html?appid={wxP.Name}").AbsoluteUri : redirect);
                            context.Token.Put("oauth_callback", oauth_callback).Commit(context.UserHostAddress, context.Server);
                            context.Redirect($"https://open.weixin.qq.com/connect/oauth2/authorize?appid={appids[0]}&response_type=code&scope=snsapi_base&state={transfer}&redirect_uri={redirect_uri}#wechat_redirect");//, Uri.EscapeDataString(new Uri(reDomain,$"/weixin?appid={appids[0]}").AbsoluteUri), appids[0]));
                        }
                    }
                    else
                    {
                        Error(context, "微信使用提示", "缺少微信参数，请联系管理员");
                    }
                }
                else if (ua.Contains("DINGTALK"u8, true))
                {
                    var account = UMC.Data.Reflection.Configuration("account");
                    var appids = new List<String>();
                    foreach (var p in account.Providers)
                    {
                        if (String.Equals(p.Type, "dingtalk"))
                        {
                            var loginUrl = p["loginUrl"];
                            if (String.IsNullOrEmpty(loginUrl) == false)
                            {
                                context.Redirect(new Uri(loginUrl.Replace("{device}", $"AUTO_{transfer}")).AbsoluteUri);
                                return;
                            }
                            appids.Add(p.Name);
                        }
                    }
                    if (appids.Count == 0)
                    {
                        Error(context, "钉钉使用提示", "缺少钉钉参数，请联系管理员");

                    }
                    else
                    {
                        context.StatusCode = 200;
                        context.AddHeader(StringValue.CacheControl, "no-store");
                        context.AddHeader(StringValue.Pragma, "no-store");
                        context.ContentType = StringValue.ContentTypeHtml;// "text/html; charset=UTF-8";
                        using (System.IO.Stream stream = typeof(WebServlet).Assembly//UMC.Proxy
                                                 .GetManifestResourceStream("UMC.ITME.Resources.dingtalk.html"))
                        {
                            var str = new System.IO.StreamReader(stream).ReadToEnd();
                            var v = new System.Text.RegularExpressions.Regex("\\{(?<key>\\w+)\\}").Replace(str, g =>
                            {
                                var key = g.Groups["key"].Value.ToLower();
                                switch (key)
                                {
                                    case "appids":
                                        return UMC.Data.JSON.Serialize(appids);
                                }
                                return "";

                            });
                            context.Output.Write(v);

                        }

                    }
                }
                else
                {
                    var account = UMC.Data.Reflection.Configuration("account");
                    var appids = new List<String>();
                    var wkappids = new List<String>();
                    foreach (var p in account.Providers)
                    {

                        switch (p.Type)
                        {
                            case "dingtalk":
                            case "weixin":
                            case "wxwork":
                                break;
                            default:
                                var Agent = p["agent"];
                                if (String.IsNullOrEmpty(Agent) == false)
                                {
                                    if (ua.Contains(Agent.UTF8(), true))//, StringComparison.CurrentCultureIgnoreCase))
                                    {
                                        var loginUrl = p["loginUrl"];
                                        if (String.IsNullOrEmpty(loginUrl) == false)
                                        {
                                            context.Redirect(new Uri(loginUrl.Replace("{device}", $"AUTO_{transfer}")).AbsoluteUri);
                                            return;
                                        }
                                    }
                                }
                                break;
                        }
                    }


                    context.StatusCode = 200;
                    context.AddHeader(StringValue.CacheControl, "no-store");
                    context.AddHeader(StringValue.Pragma, "no-store");
                    var file = UMC.Data.Reflection.ConfigPath("Static/UI/Unauthorized.html");
                    var cfg = UMC.Data.Reflection.Configuration("BgSrc");
                    var bgsrcs = new List<String>();
                    var now = DateTime.Now;
                    foreach (var b in cfg.Providers)
                    {
                        int satrt = Utility.IntParse(b["start"], 0) % 24;
                        int end = Utility.IntParse(b["end"], 0) % 24;
                        if (satrt <= now.Hour && end >= now.Hour)
                        {
                            var time = b["time"];
                            bgsrcs.Add(webr.ImageResolve(context.Url, b.Name, "1", 0, $"umc-image=w3000o&{time}"));
                        }
                    }

                    var bgsrc = String.Empty;// UMC.Data.WebResource.Instance().Provider["bgsrc"];
                    if (System.IO.File.Exists(file) == false)
                    {
                        this.LocalResources(context, "/UI/Unauthorized.html", true);
                    }
                    else
                    {
                        if (bgsrcs.Count > 0)
                        {
                            bgsrc = $" bgsrc=\"{bgsrcs[now.Second % bgsrcs.Count]}\"";
                        }
                        var appName = String.Empty;
                        if (oauth_callback.StartsWith("https://") || oauth_callback.StartsWith("http://"))
                        {
                            var host = new StringValue(new Uri(oauth_callback).Host);
                            var siteInfo = GetSiteInfo(host);
                            if (siteInfo.Site != null)
                            {
                                appName = $" app=\"{siteInfo.Site.Caption}\"";
                            }
                        }
                        context.ContentType = StringValue.ContentTypeHtml;
                        var compress = HttpProxy.Compress(context, context.OutputStream);
                        using (System.IO.FileStream stream = System.IO.File.OpenRead(file))
                        {
                            var url = $"{bgsrc}{appName}".UTF8();

                            var title = String.Format("{0} · ", AppConf["title"] ?? String.Empty).UTF8();
                            byte[] array = ArrayPool<byte>.Shared.Rent(1024);
                            try
                            {
                                var isHeader = true;
                                int count;
                                while ((count = stream.Read(array, 0, array.Length)) > 0)
                                {
                                    if (isHeader)
                                    {
                                        isHeader = false;
                                        var header = array.AsSpan();

                                        int l = header.IndexOf("<head>"u8) + 5;
                                        compress.Write(array, 0, l);
                                        compress.Write(url, 0, url.Length);

                                        int c = header.IndexOf("<title>"u8) + 7;
                                        compress.Write(array, l, c - l);
                                        compress.Write(title, 0, title.Length);
                                        compress.Write(array, c, count - c);

                                    }
                                    else
                                    {
                                        compress.Write(array, 0, count);
                                    }
                                }
                            }
                            finally
                            {
                                ArrayPool<byte>.Shared.Return(array);
                                stream.Close();

                            }
                        }
                        compress.Flush();
                        compress.Close();
                        context.OutputFinish();
                    }
                }
            }
        }

        public static Set<SiteConfig.PageConfig<int>> HighFrequency
        {
            get;
            private set;
        } = new Set<SiteConfig.PageConfig<int>>(SiteConfig.PageConfig<int>.comparer);
        protected virtual void Dns(Net.NetContext context)
        {
            switch (context.Method)
            {
                case HttpMethod.GET:
                    {
                        if (context.QueryString.TryGet("dns"u8, out var dns))
                        {
                            context.ContentType = new StringValue("application/dns-message");

                            byte[] requestData = System.Buffers.ArrayPool<byte>.Shared.Rent(dns.Length + 600);

                            int size = Utility.DecodeBase64(dns.Value.Span, requestData);
                            DNS(context, requestData, size);

                        }
                        else
                        {
                            context.StatusCode = 403;
                            context.ContentType = StringValue.ContentTypeHtml;
                            context.Output.Write("缺少dns参数");
                            context.OutputFinish();
                        }
                    }
                    break;
                case HttpMethod.POST:
                    {
                        byte[] requestData = System.Buffers.ArrayPool<byte>.Shared.Rent((int)(context.ContentLength.Value + 600));
                        int size = 0;
                        context.UseSynchronousIO();
                        context.ReadAsData((b, c, l) =>
                        {
                            if (l == 0)
                            {
                                context.ContentType = new StringValue("application/dns-message");

                                DNS(context, requestData, size);

                            }
                            else
                            {
                                Array.Copy(b, c, requestData, size, l);
                                size += l;
                            }

                        });
                    }
                    break;
            }
        }
        async void DNS(NetContext context, byte[] requestData, int size)
        {
            try
            {
                ushort questionCount = System.Buffers.Binary.BinaryPrimitives.ReadUInt16BigEndian(requestData.AsSpan(4));
                if (questionCount == 0)
                {
                    context.OutputFinish();
                    return;
                }
                if (IPAddress.TryParse(context.UserHostAddress, out var address) == false)
                {
                    context.OutputFinish();
                    return;
                }
                var clientId = address.GetAddressBytes();

                // 提取查询的域名 
                StringValue domainName = DnsServer.ParseDomainName(requestData);

                var domains = DnsServer.ParentDomain(domainName);

                var paths = context.Path.Split((byte)'/');//[0].Span.SequenceEqual("dns"u8))
                if (paths.Length == 3)
                {
                    paths[1].Span.CopyTo(requestData.AsSpan(400));
                    var size2 = paths[1].Length + 400;
                    AppSecret.Span.CopyTo(requestData.AsSpan(size2));
                    size2 += AppSecret.Length;

                    var baseLen = Utility.DecodeBase64(paths[2].Span, requestData.AsSpan(size2));

                    int size3 = size2 + baseLen;//.Length;
                    size2 += 4;
                    Utility.MD5(requestData.AsSpan(400, size2 - 400), requestData.AsSpan(size3));
                    bool IsSite = true;
                    if (Utility.IntParse(requestData.AsSpan(size3, 16)).StringValue().Span.SequenceEqual(requestData.AsSpan(size2, 4)))
                    {

                        if (domainName.Span.EndsWith(Socks5Server.ProxyDomain.Span))
                        {
                            var indexs = new Dictionary<uint, bool>();
                            var ms = HotCache.Find(new ProxySite());
                            indexs.Add(0, false);
                            foreach (var m in ms)
                            {
                                var value3 = m.ProxyIP.UInt32() ?? 0;
                                if (value3 != 0)
                                {
                                    if (indexs.TryGetValue(value3, out var value33))
                                    {
                                        if (m.Type.Int32() == 1)
                                        {
                                            if (value33 == false)
                                            {
                                                indexs[value3] = true;
                                            }
                                        }
                                    }
                                    else
                                    {
                                        indexs[value3] = m.Type.Int32() == 1;
                                    }
                                }
                            }

                            var proxys = indexs.Keys.ToArray();
                            Array.Sort(proxys);
                            var addreses = Array.Empty<IPAddress>();

                            var value = domainName.Slice(0, domainName.Length - Socks5Server.ProxyDomain.Length - 1);
                            if (value.IsEmpty == false)
                            {
                                int v = value.Span.IndexOf((byte)'-');
                                if (v == -1)
                                {
                                    v = value.Span.IndexOf((byte)'.');
                                }
                                if (v > -1 && Utility.TryParse(value.Slice(v + 1).Span, 10, out var proxyId))
                                {
                                    if (proxyId < proxys.Length && indexs[proxys[proxyId]])
                                    {
                                        addreses = new IPAddress[] { new IPAddress(proxys[proxyId].StringValue().Span) };
                                    }
                                }
                            }
                            size3 = DnsServer.BuildDnsResponse(requestData, domainName, 60, addreses);
                            context.ContentLength = size3;
                            context.ContentType = new StringValue("application/dns-message");
                            context.OutputStream.Write(requestData, 0, size3);
                            context.OutputFinish();
                            return;
                        }
                        var proxySite = HotCache.Find(new ProxySite { Domain = domainName }, "Domain", domains).OrderByDescending(d => d.Domain.Length).FirstOrDefault();
                        uint tls = 60;// DnsServer.TLSTIME;
                        if (proxySite != null)
                        {
                            var username = paths[1];// new StringValue($"{paths[1]}@{AppIdKey}");
                            var addreses = Array.Empty<IPAddress>();
                            var ProxyType = proxySite.ProxyIP.UInt32() ?? 0;


                            if (this.CheckUser(proxySite.ConfigKey, username, proxySite.Domain) == false)
                            {
                                addreses = await NetProxy.DNSToIPAsync(domainName.UTF8(), new CancellationTokenSource(5000).Token);
                            }
                            else
                            {

                                if (ProxyType != 0 && proxySite.Type.Int32() == 1)
                                {

                                    int value = 0;
                                    if (proxySite.ProxyIP.Length > 4)
                                    {
                                        Utility.MD5(domainName.Span, requestData.AsSpan(400));

                                        var md5 = Utility.IntParse(requestData.AsSpan(400, 16)).StringValue();
                                        var query = proxySite.RecursiveQuery ?? StringValue.Empty;

                                        int iplen = proxySite.ProxyIP.Length;
                                        if (iplen < query.Length)// >proxySite.ProxyIP.Length)
                                        {
                                            query = query.Slice(0, iplen);
                                        }
                                        value = query.Span.IndexOf(md5.Span);
                                        if (value == -1)
                                        {
                                            if (query.Length != iplen)
                                            {
                                                if (query.Length < iplen)
                                                {
                                                    var t = new byte[query.Length + 4];
                                                    query.Span.CopyTo(t);
                                                    query = new StringValue(t);
                                                }
                                                else
                                                {
                                                    query = query.Slice(0, iplen);
                                                }
                                                md5.Span.CopyTo(query.Span.Slice(query.Length - 4));
                                            }
                                            else
                                            {
                                                query.Span.Slice(4, query.Length - 4).CopyTo(query.Span);
                                                md5.Span.CopyTo(query.Span.Slice(query.Length - 4));
                                            }

                                            HotCache.Put(new ProxySite
                                            {
                                                Domain = proxySite.Domain,
                                                RecursiveQuery = query,//new StringValue(t)
                                            });
                                            value = (query.Length - 4) / 4 % (proxySite.ProxyIP.Length / 4);
                                        }
                                        else
                                        {
                                            value = (value / 4) % (proxySite.ProxyIP.Length / 4);
                                        }
                                    }

                                    addreses = new IPAddress[] { new IPAddress(proxySite.ProxyIP.Slice(value * 4, 4).Span) };

                                    requestData[400] = 0x02;
                                    requestData[401] = 0x04;
                                    requestData[402] = 0x00;
                                    requestData[403] = (byte)domainName.Length;
                                    domainName.Span.CopyTo(requestData.AsSpan(404));
                                    var length = domainName.Length + 404;
                                    requestData[length] = (byte)clientId.Length;
                                    clientId.CopyTo(requestData.AsSpan(length + 1));
                                    length += 1 + clientId.Length;
                                    requestData[length] = (byte)username.Length;
                                    username.Span.CopyTo(requestData.AsSpan(length + 1));
                                    length += 1 + username.Length;
                                    requestData[length] = 0x00;
                                    length++;
                                    tls = 1200;
                                    try
                                    {
                                        using (var tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
                                        {
                                            var timeout = new CancellationTokenSource(5000);
                                            await tcpClient.ConnectAsync(addreses[0], 443, timeout.Token);
                                            await tcpClient.SendAsync(new ArraySegment<byte>(requestData, 400, length - 400), timeout.Token);
                                        }
                                    }
                                    catch
                                    {
                                        IsSite = false;
                                    }

                                }
                                else if (Socks5Server.IsInternalIP(clientId) == false)
                                {
                                    UMC.Bridge.SocksUser.Cache.Put(new UMC.Bridge.SocksUser
                                    {
                                        ClientIP = new StringValue(clientId),
                                        Port = ((ushort)443).StringValue(),
                                        UserAgent = username,// proxySite.Domain,
                                        Username = username,//new StringValue($"{paths[1]}@{AppIdKey}"),
                                        ActiveTime = new StringValue(BitConverter.GetBytes(UMC.Data.Utility.TimeSpan())),
                                        ExpirationTime = new StringValue(BitConverter.GetBytes(UMC.Data.Utility.TimeSpan(DateTime.Now.AddHours(1))))
                                    });
                                }
                                if (IsSite == false)
                                {
                                    addreses = await NetProxy.DNSToIPAsync(domainName.UTF8(), new CancellationTokenSource(5000).Token);
                                }
                            }

                            int contentLength = DnsServer.BuildDnsResponse(requestData, domainName, tls, addreses);
                            context.ContentLength = contentLength;
                            context.ContentType = new StringValue("application/dns-message");
                            context.OutputStream.Write(requestData, 0, contentLength);
                            context.OutputFinish();
                            return;

                        }
                    }
                }
                try
                {
                    var timeOut = new CancellationTokenSource(5000);
                    using (UdpClient client2 = new UdpClient(DnsServer.IterativeDNS.AddressFamily))
                    {
                        await client2.SendAsync(requestData.AsMemory(0, size), DnsServer.IterativeDNS, timeOut.Token);
                        var res = await client2.ReceiveAsync(timeOut.Token);
                        var clen = res.Buffer.Length;
                        context.ContentLength = clen;
                        context.ContentType = new StringValue("application/dns-message");
                        context.OutputStream.Write(res.Buffer, 0, clen);
                    }
                }
                finally
                {
                    context.OutputFinish();

                }
            }
            catch
            {

            }
            finally
            {
                System.Buffers.ArrayPool<byte>.Shared.Return(requestData);

            }


        }
        public static void Error(Net.NetContext context, int statusCode, String title, String msg, String log)
        {
            context.StatusCode = statusCode;
            context.ContentType = StringValue.ContentTypeHtml;
            context.AddHeader(StringValue.CacheControl, "no-store");
            using (System.IO.Stream stream = typeof(WebServlet).Assembly
                                        .GetManifestResourceStream("UMC.ITME.Resources.error.html"))
            {
                var str = new System.IO.StreamReader(stream).ReadToEnd();
                var v = new System.Text.RegularExpressions.Regex("\\{(?<key>\\w+)\\}").Replace(str, g =>
                {
                    var key = g.Groups["key"].Value.ToLower();
                    switch (key)
                    {
                        case "title":
                            return title;
                        case "msg":
                            return msg;
                        case "log":
                            return log;

                    }
                    return "";

                });
                context.Output.Write(v);


            }
        }
        public static void Error(Net.NetContext context, String title, String msg, String log)
        {
            Error(context, 401, title, msg, log);
        }

        public static void Error(Net.NetContext context, Site site, String title, String msg)
        {

            var dir = Data.Reflection.ConfigPath("Static");
            var file = UMC.Data.Utility.FilePath($"{dir}/Error/{site.Root}.html");
            if (File.Exists(file))
            {
                context.StatusCode = 401;
                using (System.IO.FileStream stream = System.IO.File.OpenRead(file))
                {
                    byte[] array = ArrayPool<byte>.Shared.Rent(1024);
                    try
                    {
                        context.ContentType = StringValue.ContentTypeHtml;
                        context.ContentLength = stream.Length;
                        int count;
                        while ((count = stream.Read(array, 0, array.Length)) > 0)
                        {
                            context.OutputStream.Write(array, 0, count);
                        }
                    }
                    finally
                    {
                        ArrayPool<byte>.Shared.Return(array);
                        stream.Close();
                    }

                }
            }
            else
            {

                Error(context, 401, title, msg, "");
            }
        }
        public static void Error(Net.NetContext context, String title, String msg)
        {
            Error(context, 401, title, msg, "");
        }
        void Auth(Net.NetContext context, string wk)
        {
            context.ContentType = StringValue.ContentTypeHtml;

            using (System.IO.Stream stream = typeof(WebServlet).Assembly
                                                   .GetManifestResourceStream($"UMC.ITME.Resources.{wk}.html"))
            {

                switch (wk)
                {
                    case "dingtalk":
                        {
                            var account = UMC.Data.Reflection.Configuration("account");
                            var appids = new List<String>();
                            foreach (var p in account.Providers)
                            {
                                if (String.Equals(p.Type, wk))
                                {
                                    appids.Add(p.Name);
                                }
                            }
                            var str = new System.IO.StreamReader(stream).ReadToEnd();
                            var v = new Regex("\\{(?<key>\\w+)\\}").Replace(str, g =>
                            {
                                var key = g.Groups["key"].Value.ToLower();
                                switch (key)
                                {
                                    case "appids":
                                        return UMC.Data.JSON.Serialize(appids);

                                }
                                return "";

                            });
                            context.Output.Write(v);
                        }
                        break;
                    default:
                        stream.CopyTo(context.OutputStream);
                        break;
                }

            }
        }
        String NewCookie(NetContext context, bool isAutoCookie = false)
        {
            var sessionKey = Utility.Guid(Utility.NewGuid());


            if (isAutoCookie || context.Headers.Get(StringValue.SecFetchDest.Span).Span.SequenceEqual("document"u8))
            {
                var cdmn = MainDomainValue;
                var cdm = cdmn;
                if (DomainUnion == '-')
                {
                    cdm = cdmn.Slice(cdmn.Span.IndexOf((byte)'.'));
                }
                if (context.Scheme.Span.SequenceEqual(StringValue.Https.Span))
                {
                    if (context.Host.Span.EndsWith(cdm.Span))
                    {
                        context.AddHeader(new HeadValue(StringValue.SetCookie, context.Memory.Rent($"{SessionCookieName}={sessionKey}; SameSite=None; Secure; Expires={DateTime.Now.AddYears(10):r}; HttpOnly; Domain={cdm}; Path=/")));
                    }
                    else
                    {
                        context.AddHeader(new HeadValue(StringValue.SetCookie, context.Memory.Rent($"{SessionCookieName}={sessionKey}; SameSite=None; Secure; HttpOnly; Path=/")));
                    }
                }
                else
                {
                    if (context.Host.Span.EndsWith(cdm.Span))
                    {
                        context.AddHeader(new HeadValue(StringValue.SetCookie, context.Memory.Rent($"{SessionCookieName}={sessionKey}; Expires={DateTime.Now.AddYears(10):r}; HttpOnly; Domain={cdm}; Path=/")));
                    }
                    else
                    {
                        context.AddHeader(new HeadValue(StringValue.SetCookie, context.Memory.Rent($"{SessionCookieName}={sessionKey}; HttpOnly; Path=/")));
                    }
                }
            }
            return sessionKey;


        }
        String GetCookie(NetContext context)
        {
            var cookies = context.Cookies.GetValues("device"u8);
            if ((cookies?.Length ?? 0) == 0)
            {
                return NewCookie(context);
            }

            return cookies[cookies.Length - 1].Span.UTF8();
        }

        void Synch(NetContext context)
        {
            var paths = new List<String>(context.Path.Span.UTF8().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries));

            var sessionKey = GetCookie(context);

            var deviceId = Data.Utility.Guid(sessionKey, true).ToString();

            if (paths.Count > 1)
            {
                if (String.Equals(paths[1], context.Server))
                {
                    if (paths.Count > 2 && String.Equals(paths[2], "UMC.Auth"))
                    {
                        context.Redirect(context.RawUrl.Slice(context.Path.Span.IndexOf("/UMC.Auth/"u8)).UTF8());
                    }
                    else
                    {
                        context.Redirect("/UMC.Login");
                    }
                    return;
                }
                else if (paths.Count > 3 && String.Equals(paths[2], "UMC.Auth"))
                {
                    deviceId = paths[3];
                }

            }
            else
            {
                context.Redirect("/UMC.Login");
                return;
            }
            var sub = Net.NetSubscribe.Subscribes.FirstOrDefault(r => String.Equals(r.Key, paths[1]));
            if (sub == null)
            {
                context.Redirect("/UMC.Login");
                return;
            }
            var p = UMC.Data.WebResource.Instance();
            var secret = p.Provider["appSecret"];
            var appId = p.Provider["appId"];
            var type = typeof(UMC.Data.Entities.Session).FullName;


            var webD = new Web.WebMeta();
            webD.Put("from", appId);
            webD.Put("time", UMC.Data.Utility.TimeSpan().ToString());
            webD.Put("type", type);
            webD.Put("sign", UMC.Data.Utility.Sign(webD, secret));
            webD.Put("value", new WebMeta().Put("SessionKey", deviceId));

            var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent(52);
            BitConverter.TryWriteBytes(buffer.AsSpan(16), Utility.TimeSpan());
            AppId.Span.CopyTo(buffer.AsSpan(20));
            AppSecret.Span.CopyTo(buffer.AsSpan(36));

            Utility.MD5(buffer.AsSpan(16, 36), buffer.AsSpan(0, 16));

            var webr = new Uri($"https://{sub.Address}/UMC.Synch").WebRequest(context.Memory);
            webr.Add("If-Challenge", Utility.ToBase64String(buffer.AsSpan(0, 16)));

            System.Buffers.ArrayPool<byte>.Shared.Return(buffer);
            context.UseSynchronousIO();
            webr.Post(webD, xhr =>
            {
                xhr.ReadAsString(xhrStr =>
                {
                    if (xhr.StatusCode == HttpStatusCode.OK)
                    {
                        var seesion = JSON.Deserialize<UMC.Data.Entities.Session>(xhrStr);

                        UMC.Data.DataFactory.Instance().Put(seesion);
                        if (paths.Count > 2 && String.Equals(paths[2], "UMC.Auth"))
                        {
                            context.Redirect(context.RawUrl.Slice(context.Path.Span.IndexOf("/UMC.Auth/"u8)).UTF8());
                        }
                        else
                        {
                            context.Redirect("/UMC.Login");
                        }

                    }
                    else
                    {
                        context.Redirect("/UMC.Login");

                    }
                    context.OutputFinish();
                }, e =>
                {
                    context.Redirect("/UMC.Login");
                    context.OutputFinish();
                });
            });
        }
        void Transfer(SiteHost hostSite, SiteConfig siteConfig, UMC.Net.NetContext context, int? StaticPage)
        {
            switch (hostSite.Scheme ?? 0)
            {
                case 0:
                    break;
                case 2:
                    if (context.Scheme.Length == 4)
                    {
                        context.Redirect($"https://{context.Url.Host}{context.RawUrl}");
                        return;
                    }
                    break;
            }

            if (siteConfig == null)
            {
                UMC.ITME.DataFactory.Instance().Delete(hostSite);
                context.Redirect(context.Url.ToString());
                return;
            }
            if (siteConfig.Site.OpenModel == 3)
            {
                context.Redirect(new Uri(new Uri(siteConfig.Domains[0]), context.RawUrl.UTF8()).AbsoluteUri);
                return;
            }
            if (siteConfig.AllowAllPath == false)
            {
                context.Token = this.AccessToken(context);
                if (context.Token.IsAuthenticated == false)
                {
                    var seesionKey = UMC.Data.Utility.Guid(context.Token.Device.Value);
                    context.Redirect(new Uri(AuthDomain(context), $"/Unauthorized?oauth_callback={Uri.EscapeDataString(context.Url.AbsoluteUri)}&transfer={seesionKey}").AbsoluteUri);
                    return;
                }
                if (context.Method == HttpMethod.GET)
                {
                    var domain = WebServlet.MainDomain;

                    var hostModel = siteConfig.Site.HostModel ?? HostModel.None;
                    var union = WebServlet.DomainUnion;
                    var scheme = Data.WebResource.Instance().Provider["scheme"] ?? "http";

                    if (HttpProxy.CheckPath(context.Path.Span, out var _RePath, siteConfig.RedirectPath))
                    {
                        var mainKey = String.Format("SITE_JS_CONFIG_{0}{1}", hostSite.Root, HttpProxy.MD5(_RePath)).ToUpper();

                        var sConf = UMC.Data.DataFactory.Instance().Config(mainKey)?.ConfValue ?? StringValue.Empty;//.UTF8();
                        if (sConf.Length > 0)
                        {

                            var wr = WebTransfer(siteConfig, context);
                            context.UseSynchronousIO();

                            wr.Net(context, xhr =>
                            {
                                if (xhr.StatusCode == HttpStatusCode.OK && xhr.ContentType.Contains("text/html"u8) == true)
                                {
                                    var ContentEncoding = xhr.Headers.Get(StringValue.ContentEncoding.Span);//.Span.UTF8();
                                    xhr.Header(context);
                                    xhr.ReadAsStream(content =>
                                   {
                                       using (var outStream = DataFactory.Instance().Compress(context.OutputStream, ContentEncoding))
                                       {
                                           using (var f = DataFactory.Instance().Decompress(content, ContentEncoding))
                                           {
                                               f.CopyTo(outStream);
                                           }
                                           ;
                                           var writer = new UMC.Net.TextWriter(outStream.Write);
                                           writer.Write("<script id=\"Site\" root=\"");
                                           writer.Write(hostSite.Root);
                                           writer.WriteLine("\">");

                                           writer.Flush();
                                           sConf.Output(outStream.Write);
                                           //    outStream.Write(sConf);
                                           writer.WriteLine("</script>");
                                           writer.Flush();
                                           writer.Dispose();
                                           outStream.Flush();
                                           content.Close();
                                           content.Dispose();
                                       }
                                       context.OutputFinish();
                                   }, context.Error);

                                }
                                else
                                {
                                    xhr.Transfer(context);
                                }
                            });
                            return;
                        }
                        else
                        {
                            context.Redirect($"{scheme}://{hostSite.Root}{union}{domain}{context.RawUrl}");
                            return;
                        }
                    }

                    switch (hostModel)
                    {
                        case HostModel.Disable:
                            context.Redirect($"{scheme}://{hostSite.Root}{union}{domain}{context.RawUrl}");
                            return;
                        case HostModel.Check:
                            if (context.Headers.TryGet("accept-language"u8, out var _)
                                && context.Headers.TryGet("accept-encoding"u8, out var _))
                            {
                                context.Redirect($"{scheme}://{hostSite.Root}{union}{domain}{context.RawUrl}");
                                return;
                            }
                            break;
                        case HostModel.Login:

                            if (HttpProxy.IsLoginPath(siteConfig, context.PathQuery))
                            {
                                context.Redirect($"{scheme}://{hostSite.Root}{union}{domain}{context.RawUrl}");
                                return;
                            }
                            break;
                        case HostModel.None:


                            break;
                    }
                }
            }
            Transfer(CheckSubSite(context, siteConfig), context, StaticPage);

        }
        void Cert(NetContext context)
        {
            var secret = UMC.Data.WebResource.Instance().Provider["appSecret"];
            context.UseSynchronousIO();
            switch (context.Method)
            {
                case HttpMethod.GET:
                    var sign = context.QueryString.Get("sign");
                    if (String.IsNullOrEmpty(sign))
                    {
                        HtmlResource(context, "app");
                        context.OutputFinish();
                    }
                    else
                    {
                        var p = UMC.Data.WebResource.Instance();
                        String appId = p.Provider["appId"];

                        var time = context.QueryString.Get("time");
                        var nvs = new NameValueCollection
                        {
                            { "appId", appId },
                            { "time", time }
                        };

                        var webMeta = new WebMeta();
                        context.ContentType = StringValue.ContentTypeText;
                        if (String.Equals(sign, UMC.Data.Utility.Sign(nvs, secret)))
                        {
                            var Nonce = Utility.Guid(Guid.NewGuid());
                            nvs.Add("nonce", Nonce);
                            webMeta.Put("nonce", Nonce);
                            webMeta.Put("sign", UMC.Data.Utility.Sign(nvs, secret));
                            webMeta.Put("msg", "验证通过").Put("code", "success");
                        }
                        else
                        {
                            context.StatusCode = 405;
                            webMeta.Put("msg", "域名所有权签名验证不通过").Put("code", "error");
                        }
                        UMC.Data.JSON.Serialize(webMeta, context.Output);
                        context.OutputFinish();
                    }

                    return;
                case HttpMethod.PUT:
                    UMC.Data.JSON.Serialize(new WebMeta("appId", UMC.Data.WebResource.Instance().Provider["appId"], "domain", MainDomain).Put("Ed25519", Utility.ToBase64String(Ed25519.PublicKey.Export(KeyBlobFormat.RawPublicKey))), context.Output);
                    context.OutputFinish();
                    break;
                default:

                    Utility.Sign(new Uri(APIProxy.Uri, "Certificater").WebRequest(context.Memory), secret)
                    .Post(new WebMeta().Put("type", "cert", "domain", context.Url.Host), r =>
                    {
                        if (r.StatusCode == HttpStatusCode.OK)
                        {
                            Utility.Certificate(r);

                            context.Output.Write("OK");

                            context.OutputFinish();
                        }
                        else
                        {
                            r.Transfer(context);
                        }
                    });
                    break;
            }
        }
        protected override void IndexResource(NetContext context)
        {
            if (context.Path.Span.EndsWith(".html"u8))
            {
                base.IndexResource(context);
            }
            else
            {
                context.ContentType = StringValue.ContentTypeHtml;
                using (System.IO.Stream stream = typeof(WebServlet).Assembly
                                   .GetManifestResourceStream("UMC.ITME.Resources.desktop.ui.html"))
                {

                    var title = String.Format("{0} · ", AppConf["title"] ?? String.Empty).UTF8();

                    var compress = HttpProxy.Compress(context, context.OutputStream);
                    byte[] array = ArrayPool<byte>.Shared.Rent(1024);
                    try
                    {
                        var isHeader = true;
                        int count;
                        while ((count = stream.Read(array, 0, array.Length)) > 0)
                        {
                            if (isHeader)
                            {
                                isHeader = false;
                                int l = array.AsSpan().IndexOf("<title>"u8) + 7;
                                compress.Write(array, 0, l);
                                compress.Write(title, 0, title.Length);
                                compress.Write(array, l, count - l);

                            }
                            else
                            {
                                compress.Write(array, 0, count);
                            }
                        }
                        compress.Flush();
                    }
                    finally
                    {
                        compress.Close();
                        ArrayPool<byte>.Shared.Return(array);

                    }
                }
            }
        }
        void WebSocket(NetContext context)
        {
            if (context.Method == HttpMethod.GET)
            {
                if (context.Path.Length > 8)
                {
                    var sessionKey = Utility.Guid(context.Path.Slice(8).Span.UTF8(), true).Value;
                    JSON.Serialize(new WebMeta().Put("online", UMC.Host.HttpWebSocket.Online(sessionKey)), context.Output);
                }
                else
                {
                    JSON.Serialize(new WebMeta().Put("online", false), context.Output);
                }
            }
            else
            {
                context.UseSynchronousIO();
                context.ReadAsStream(ms =>
                {
                    using (var rnew = new System.IO.StreamReader(ms))
                    {
                        var str = rnew.ReadToEnd();
                        UMC.Host.HttpWebSocket.Send(Utility.Guid(context.Path.Slice(8).Span.UTF8(), true).Value, str);
                    }
                    context.OutputFinish();
                }, context.Error);
            }

        }
        void UI(NetContext context)
        {

            var Root = $"/{context.Path.Slice(5)}";//.Span.UTF8();
            if (Root.EndsWith(".html"))
            {
                var dir = UMC.Data.Reflection.ConfigPath("Static");
                var filename = UMC.Data.Utility.FilePath(dir + Root);
                if (System.IO.File.Exists(filename))
                {

                    var fileInfo = new System.IO.FileInfo(filename);
                    var Modified = context.Memory.Rent(fileInfo.LastWriteTimeUtc.ToString("r"));
                    context.ContentType = StringValue.ContentTypeHtml;
                    context.AddHeader(new HeadValue(StringValue.LastModified, Modified));
                    var Since = context.Headers.Get("If-Modified-Since"u8);
                    if (Since.IsEmpty == false)
                    {
                        if (Modified.Span.SequenceEqual(Since.Span))
                        {
                            context.StatusCode = 304;
                            return;

                        }
                    }
                    context.AddHeader(StringValue.CacheControl, "public");

                    using (System.IO.FileStream stream = System.IO.File.OpenRead(fileInfo.FullName))
                    {
                        byte[] array = ArrayPool<byte>.Shared.Rent(1024);
                        try
                        {
                            int count = stream.Read(array, 0, array.Length);
                            if (count > 0)
                            {
                                if (array.AsSpan().StartsWith("<div "u8))
                                {
                                    var appId = UMC.Data.WebResource.Instance().Provider["appId"];
                                    var sb = new System.Text.StringBuilder();
                                    sb.AppendLine();
                                    sb.Append("version=\"");
                                    sb.Append(APIProxy.Version);
                                    if (!String.IsNullOrEmpty(appId))
                                    {
                                        sb.Append("|");
                                        sb.Append(appId);
                                    }
                                    sb.AppendLine("\" ");
                                    var bytes = sb.ToString().UTF8();
                                    context.ContentLength = fileInfo.Length + bytes.Length;
                                    context.OutputStream.Write(array, 0, 4);
                                    context.OutputStream.Write(bytes, 0, bytes.Length);
                                    context.OutputStream.Write(array, 4, count - 4);
                                }
                                else
                                {
                                    context.ContentLength = fileInfo.Length;
                                    context.OutputStream.Write(array, 0, count);
                                }
                            }
                            while ((count = stream.Read(array, 0, array.Length)) > 0)
                            {
                                context.OutputStream.Write(array, 0, count);
                            }
                        }
                        finally
                        {
                            ArrayPool<byte>.Shared.Return(array);
                        }
                    }
                    context.OutputFinish();
                }
                else
                {
                    LocalResources(context, Root, true);
                }

            }
            else
            {
                LocalResources(context, Root, true);
            }


        }
        void ConfigJs(NetContext context)
        {
            var cookies = context.Cookies.GetValues("device"u8);

            if ((cookies?.Length ?? 0) == 0)
            {
                NewCookie(context, true);
            }
            context.ContentType = StringValue.ContentTypeJS;

            context.Output.Write("UMC.UI.Config({possrc:'");

            context.Output.Write("/UMC.'");
            var webr = Data.WebResource.Instance();
            var domain = webr.WebDomain();
            context.Output.Write(",posurl: '");
            var host2 = context.Url.Host;

            if (String.Equals("localhost", domain) == false)
            {
                if (host2.EndsWith(domain) && host2.Length > domain.Length)
                {
                    var sroot = host2.Substring(0, host2.Length - domain.Length - 1);
                    var psite2 = UMC.ITME.DataFactory.Instance().SiteConfig(sroot);
                    if (psite2 != null)
                    {
                        context.Output.Write("/UMC.Core/'");
                        context.Output.Write(",'root':'{0}'", psite2.Site.Root);
                        context.Output.Write(",'site':'{0}'", psite2.Site.SiteKey ?? 0);
                        context.Output.Write(",'title':");

                        UMC.Data.JSON.Serialize(psite2.Caption, context.Output);
                    }
                    else
                    {
                        context.Output.Write("/UMC/'");
                    }
                }
                else
                {
                    context.Output.Write("/UMC/'");
                    if (UMC.ITME.WebServlet.AppConf.ContainsKey("title"))
                    {
                        context.Output.Write(",'title':");
                        UMC.Data.JSON.Serialize(UMC.ITME.WebServlet.AppConf["title"], context.Output);
                    }
                }
                var auth = webr.Provider["auth"];
                if (String.IsNullOrEmpty(auth))
                {
                    context.Output.Write(",'domain':'{0}://{1}'", context.Url.Scheme, domain);
                }
                else
                {
                    context.Output.Write(",'domain':'{0}://{1}.{2}'", context.Url.Scheme, auth, domain);
                }
            }
            else
            {
                context.Output.Write("/UMC/'");
            }

            context.Output.WriteLine("});");
            context.Output.WriteLine("UMC.Src = '/UMC.UI/';");
        }
        void Conf(NetContext context)
        {
            if (context.Path.Length > 12)
            {

                var Path = context.Path.Span.UTF8();
                var keyIndex2 = Path.IndexOf('/', 11);
                if (keyIndex2 > 0)
                {
                    var psite = UMC.ITME.DataFactory.Instance().SiteConfig(Path.Substring(10, keyIndex2 - 10));
                    if (psite != null)
                    {
                        context.AddHeader(StringValue.LastModified, DateTime.Now.ToString("r"));
                        context.AddHeader(StringValue.CacheControl, "max-age=1000");
                        var pathkey = Path.Substring(Path.LastIndexOf('/') + 1);
                        var extIndex = pathkey.LastIndexOf('.');
                        if (extIndex > 0)
                        {
                            pathkey = pathkey.Substring(0, extIndex);
                        }

                        var mainKey = String.Format("SITE_JS_CONFIG_{0}{1}", psite.Root, pathkey).ToUpper();
                        var config = UMC.Data.DataFactory.Instance().Config(mainKey)?.ConfValue ?? StringValue.Empty;
                        context.ContentType = StringValue.ContentTypeJS;
                        if (config.Length > 0)
                        {
                            config.Output(context.OutputStream.Write);
                        }

                    }
                }
            }
            else
            {
                ConfigJs(context);
            }
        }
        void PathAuth(NetContext context)
        {
            var sessionKey = Utility.Guid(GetCookie(context), true).Value;
            var Path = context.Path.Slice(10);
            var key = Path.Slice("/"u8);
            var seesion = UMC.Data.DataFactory.Instance().Session(key);
            if (seesion != null)
            {
                var Value = UMC.Data.JSON.Deserialize<UMC.Data.AccessToken>(seesion.Content.Span);
                if (Value != null)
                {
                    var login = UMC.Data.Reflection.Configuration("account")?["login"];
                    var timeout = UMC.Data.Utility.IntParse(login?.Attributes?["timeout"], 3600);
                    new UMC.Data.AccessToken(sessionKey).Login(Value.Identity(), timeout).Commit("Desktop", context.UserHostAddress, context.Server);

                }
                UMC.Data.DataFactory.Instance().Delete(seesion);
            }

            context.Redirect($"{Path.Slice(key.Length)}{context.Url.Query}");
        }
        static StringValue PfxUMC = new StringValue("UMC.");
        public override void ProcessRequest(NetContext context)
        {
            var Path = context.Path.Slice(1);
            var isLoginAPI = false;
            if (Path.Span.StartsWith(PfxUMC.Span))
            {

                switch (Path.Slice("/"u8).Span.UTF8())
                {
                    case "UMC.WS":
                        WebSocket(context);
                        return;
                    case "UMC.Cert":
                        Cert(context);
                        return;
                    case "UMC.TEMP":
                        Temp(context);
                        return;
                    case "UMC.Core":
                        base.Process(context);
                        return;
                    case "UMC.For":
                        Synch(context);
                        return;
                    case "UMC.Synch":
                        Synchronize(context);
                        return;
                    case "UMC.UI":
                        UI(context);
                        return;
                    case "UMC.Home":
                        HtmlResource(context, "desktop.umc");
                        return;
                    case "UMC.TOP":
                        context.Redirect(AuthDomain(context).AbsoluteUri);
                        return;
                    case "UMC.SignOut":
                        this.AccessToken(context);
                        context.SignOut();
                        this.NewCookie(context, true);
                        context.Redirect(AuthDomain(context).AbsoluteUri);
                        return;
                    case "UMC.Reset":
                        var urlReferrer = context.UrlReferrer;
                        if (urlReferrer != null)
                        {
                            var authUrl = AuthDomain(context);
                            if (authUrl.Host == urlReferrer.Host)
                            {
                                context.Redirect(String.Format("/Unauthorized?oauth_callback={0}", Uri.EscapeDataString(urlReferrer.PathAndQuery)));

                            }
                            else
                            {
                                context.Redirect(new Uri(authUrl, String.Format("/Unauthorized?transfer={1}&oauth_callback={0}", Uri.EscapeDataString(urlReferrer.AbsoluteUri), this.GetCookie(context))).AbsoluteUri);
                            }
                        }
                        else
                        {
                            context.Redirect(AuthDomain(context).AbsoluteUri);
                        }
                        return;
                    case "UMC.Clear":
                        {
                            var hs = new List<String>(context.Url.Host.Split('.'));
                            while (hs.Count > 1)
                            {
                                context.AddHeader(StringValue.SetCookie, $"{SessionCookieName}=; Expires={DateTime.Now.AddDays(-1):r}; HttpOnly; Domain={String.Join('.', hs.ToArray())}; Path=/");
                                hs.RemoveAt(0);
                            }
                            context.AddHeader(StringValue.SetCookie, $"{SessionCookieName}=; Expires={DateTime.Now.AddDays(-1):r}; HttpOnly; Path=/");

                            context.ContentType = StringValue.ContentTypeText;

                            context.Output.WriteLine(context.Headers);
                        }
                        return;
                    case "UMC.Conf.js":
                        ConfigJs(context);
                        return;
                    case "UMC.Conf":
                        Conf(context);
                        return;
                    case "UMC.css":
                    case "UMC.js":
                        LocalResources(context, $"/{Path.Slice(4)}", true);
                        return;
                    case "UMC.Auth":
                        this.PathAuth(context);
                        return;
                    case "UMC.Login":
                        isLoginAPI = true;
                        break;

                }
            }


            var host = context.Host.Slice(":"u8);//.Span.UTF8();
            TlsSite tlsSite;
            if (context.Tag is TlsSite)
            {
                tlsSite = (TlsSite)context.Tag;
            }
            else
            {
                tlsSite = GetSiteInfo(host);
            }
            if (tlsSite.IsHostOk == false)
            {
                context.Redirect($"https://{context.Host.Slice(0, context.Host.Length - MainDomain.Length - 1)}{DomainUnion}{MainDomain}{context.RawUrl}");
                return;
            }

            if (tlsSite.IsHome == false)
            {
                var ishttps = IsHttps;
                if (tlsSite.SiteHost != null)
                {
                    if (tlsSite.SiteHost.IsAuthModel == true)
                    {
                        if (tlsSite.SiteHost.Scheme == 2)
                        {
                            ishttps = true;
                        }
                    }
                    else
                    {
                        Transfer(tlsSite.SiteHost, tlsSite.Site, context, tlsSite.StaticPage);
                        return;
                    }
                }
                if (tlsSite.Site != null)
                {

                    if (tlsSite.Site.Site.OpenModel == 3)
                    {
                        context.Redirect(new Uri(new Uri(tlsSite.Site.Domains[0]), context.RawUrl.UTF8()).AbsoluteUri);
                        return;
                    }
                    if (ishttps && context.Scheme.Length == 4)
                    {
                        context.Redirect($"https://{host}{context.RawUrl}");
                        return;
                    }
                    Proxy(context, tlsSite.Site, tlsSite.Pfx, isLoginAPI, tlsSite.StaticPage);

                    return;
                }
                else if (Utility.IsIP(host) == false && host.Span.StartsWith(AppIdKey.Span) == false)
                {
                    String v = AuthPre.IsEmpty ? String.Empty : ".";
                    Error(context, "域名未启用", $"当前域名未关联任何应用，请回到<a href=\"{context.Url.Scheme}://{AuthPre}{v}{MainDomain}{context.RawUrl}\">主域名</a>");
                    return;
                }
            }
            if (IsHttps && context.Scheme.Length == 4)
            {
                context.Redirect($"https://{host}{context.RawUrl}");
                return;
            }
            Apiumc(context);
        }
        internal static TlsSite GetSiteInfo(StringValue host)
        {
            TlsSite tlsSite = new TlsSite();
            if (host.Length >= MainDomain.Length && host.Span.EndsWith(_MainDomainValue.Span))
            {
                if ((AuthPre.IsEmpty == false & host.Span.StartsWith(AuthPre.Span)) || host.Span.SequenceEqual(_MainDomainValue.Span))
                {
                    tlsSite.IsHome = true;
                }
                else
                {
                    var rook = host.Slice(0, host.Length - MainDomain.Length - 1);
                    tlsSite.IsHostOk = host.Span[rook.Length] == WebServlet.DomainUnion;
                    int index = rook.Length - 1;
                    var rootsn = rook.Span;
                    for (; index > -1; index--)
                    {
                        if (rootsn[index] == '.' || rootsn[index] == '-')
                        {
                            break;
                        }
                    }
                    if (index > -1)
                    {
                        tlsSite.Site = UMC.ITME.DataFactory.Instance().SiteConfig(rook.Slice(index + 1));

                        if (tlsSite.Site != null)
                        {
                            tlsSite.Pfx = host.Slice(0, index);
                        }

                    }
                    else
                    {
                        tlsSite.Site = UMC.ITME.DataFactory.Instance().SiteConfig(rook);
                    }
                }
            }
            if (tlsSite.Site == null)
            {
                tlsSite.Pfx = StringValue.Empty;
                var siteHost = DataFactory.Instance().Host(host); ;
                if (siteHost != null)
                {
                    tlsSite.SiteHost = siteHost;
                    if (siteHost.Root.IsEmpty)
                    {
                        tlsSite.IsHome = true;
                    }
                    else
                    {
                        tlsSite.Site = UMC.ITME.DataFactory.Instance().SiteConfig(siteHost.Root);

                        tlsSite.IsTransfer = tlsSite.Site?.AllowAllPath ?? ((siteHost.IsAuthModel ?? false) == false);
                    }
                }
            }
            else
            {
                tlsSite.IsTransfer = tlsSite.Site.AllowAllPath;// false;
            }
            return tlsSite;

        }
        void Proxy(NetContext context)
        {
            context.ContentType = context.Memory.Rent("application/x-ns-proxy-autoconfig");
            var domains = new WebMeta();


            var proxyConfig = UMC.Data.Reflection.Configuration("proxy");


            var portMd5 = Math.Abs(Utility.IntParse(Utility.MD5(context.Path.Span)));

            var p = UMC.Data.Reflection.Configuration("host").Providers.FirstOrDefault(r => r.Type == "proxy");
            SortedDictionary<uint, Tuple<String, int, int>> keyValuePairs = new SortedDictionary<uint, Tuple<string, int, int>>
            {
                { 0, Tuple.Create(context.Host.UTF8(), Utility.IntParse(p?.Attributes?["port"], 443), Utility.IntParse(p?.Attributes?["limit"], 1)) }
            };

            foreach (var k in proxyConfig.Providers)
            {
                if (IPAddress.TryParse(k.Name, out var ip))
                {
                    keyValuePairs.Add(BitConverter.ToUInt32(ip.GetAddressBytes()), Tuple.Create(k.Name, Utility.IntParse(k.Attributes?["port"], 443), Utility.IntParse(k.Attributes?["limit"], 1)));
                    // indexProxy++;
                }

            }

            var indexs = new Dictionary<uint, bool>();
            var ms = HotCache.Find(new ProxySite());
            indexs.Add(0, true);
            foreach (var m in ms)
            {
                var prType = m.ProxyIP.UInt32() ?? 0;
                if (indexs.TryGetValue(prType, out var b))
                {
                    if (m.Type.Int32() == 1)
                    {
                        indexs[prType] = true;
                    }
                }
                else
                {
                    indexs[prType] = m.Type.Int32() == 1;
                }


            }

            var proxys = indexs.Keys.ToArray();
            Array.Sort(proxys);

            var jsProxy = new List<String>();
            var em = keyValuePairs.GetEnumerator();
            int index2 = 0;
            foreach (var k in proxys)
            {
                if (indexs[k])
                {
                    if (keyValuePairs.TryGetValue(k, out var tuple))
                    {
                        int port = tuple.Item2 + (portMd5 % tuple.Item3);
                        jsProxy.Add($"PROXY {tuple.Item1}:{port}; DIRECT");
                    }
                    else
                    {
                        var ip = new IPAddress(k.StringValue().Span);
                        jsProxy.Add($"PROXY {ip}:443; DIRECT");
                    }
                }
                else
                {
                    jsProxy.Add($"DIRECT");
                }

                domains.Put($"{index2}.proxy.apiumc.com", indexs[k] ? index2 : 0);


                index2++;
            }



            foreach (var m in ms)
            {
                var index = m.ProxyIP.UInt32() ?? 0;

                domains.Put(m.Domain.UTF8(), indexs[index] ? Array.BinarySearch(proxys, index) : 0);
            }

            context.Output.Write($"var domains=");
            UMC.Data.JSON.Serialize(domains, context.Output);
            context.Output.WriteLine(";");
            context.Output.Write($"var proxys=");
            UMC.Data.JSON.Serialize(jsProxy, context.Output);
            context.Output.WriteLine(";");

            context.Output.WriteLine("function FindProxyForURL(url, host) {if(domains.hasOwnProperty(host))return proxys[domains[host]]; var hs = host.split('.');var h = hs.shift().split('-').concat(hs); while (h.length>1){h.shift(); host = h.join('.');if(domains.hasOwnProperty(host))return proxys[domains[host]]}return \"DIRECT\";}");


        }
        protected virtual void Apiumc(NetContext context)
        {
            var Path = context.Path.Slice(1);
            if (context.Method == HttpMethod.GET)
            {

                if (Path.Span.StartsWith("log/"u8))
                {
                    context.Token = this.AccessToken(context);
                    if (context.Token.IsInRole(UMC.Security.Membership.AdminRole) == false)
                    {
                        context.Redirect("/");
                        return;
                    }
                }

            }
            var staticFile = Path.IsEmpty ? UMC.Data.Reflection.ConfigPath("Static/index.html") : UMC.Data.Reflection.ConfigPath($"Static/{Path}");

            if (System.IO.File.Exists(staticFile))
            {
                TransmitFile(context, staticFile, true);
                return;
            }
            switch (Path.Slice("/"u8).Span.UTF8())
            {
                case "Biometric":

                    context.Token = this.AccessToken(context);
                    if (context.Token.BiometricTime > 0)
                    {
                        context.Redirect(context.QueryString["oauth_callback"]);
                    }
                    else
                    {
                        if (Utility.IsApp(context.UserAgent))
                        {
                            HtmlResource(context, "biometric");
                        }
                        else
                        {
                            this.LocalResources(context, "/UI/Biometric.html", true);
                        }
                    }
                    return;
                case "Dns":
                    Dns(context);
                    return;
                case "Proxy":
                    Proxy(context);
                    return;
                case "notsupport":
                    NotSupport(context);
                    return;
                case "Unauthorized":
                    Unauthorized(context);
                    return;
                case "Auth":
                    Auth(context);
                    return;
                case "weixin":
                case "dingtalk":
                case "wxwork":
                    Auth(context, Path.Span.UTF8());
                    return;
                case "":
                    context.Redirect("/UMC");

                    return;
                case "Desktop":
                    if (Path.Length > 7)
                    {
                        HtmlResource(context, "desktop.page");
                    }
                    else
                    {
                        HtmlResource(context, "desktop");
                    }
                    return;
                case "favicon.ico":
                    context.StatusCode = 200;
                    context.ContentType = StringValue.ContentTypeImageIcon;// "image/x-icon";
                    using (System.IO.Stream stream = typeof(WebServlet).Assembly
                                       .GetManifestResourceStream("UMC.ITME.Resources.favicon.ico"))
                    {
                        context.ContentLength = stream.Length;
                        stream.CopyTo(context.OutputStream);
                    }
                    return;
                case "WebSSH":
                    WebSSH(context, Path.Slice(6).Span.UTF8());
                    return;
                case "Docs":
                    HtmlResource(context, "desktop.doc");
                    return;
                case "Apps":
                    HtmlResource(context, "desktop.app");
                    return;
                case "Setting":
                    var siteKey = Path.Slice("/"u8);//, pfxPath.Length);
                    if (Path.Length > siteKey.Length)
                    {
                        if (DataFactory.Instance().Site(UMC.Data.Utility.IntParse(siteKey.Span.UTF8(), 0)) != null)
                        {
                            HtmlResource(context, "desktop.site");
                        }
                        else
                        {
                            HtmlResource(context, "desktop.umc");
                        }
                    }
                    else
                    {
                        HtmlResource(context, "desktop.umc");
                    }
                    return;
                default:
                    base.ProcessRequest(context);
                    break;
            }

        }
        protected void BaseRequest(NetContext context)
        {
            base.ProcessRequest(context);
        }
        public static void WebHeaderConf(NetHttpRequest webR, SiteConfig siteConfig, UMC.Net.NetContext context, string account)
        {
            WebHeaderConf(webR, siteConfig.HeaderConf, context, account);
        }
        public static StringValue WebHeaderConfValue(SiteConfig.HeaderConfItem v, UMC.Net.NetContext context, string account)
        {
            switch (v.Type)
            {
                case SiteConfig.HeaderValueType.ACCOUNT:
                    if (String.IsNullOrEmpty(account) == false)
                    {
                        return context.Memory.Rent(account);
                    }
                    break;
                case SiteConfig.HeaderValueType.ROLES:
                    if (context.Token != null)
                    {
                        return context.Memory.Rent(context.Token.Roles);
                    }
                    break;
                case SiteConfig.HeaderValueType.TOKEN:
                    if (context.Token != null)
                    {
                        return context.Memory.Rent(context.Token.Device.ToString());
                    }
                    break;
                case SiteConfig.HeaderValueType.USERID:
                    if (context.Token != null)
                    {
                        return context.Memory.Rent(context.Token.UserId.ToString());
                    }
                    break;
                case SiteConfig.HeaderValueType.USERNAME:
                    if (context.Token != null)
                    {
                        return context.Memory.Rent(context.Token.Username);
                    }
                    break;
                case SiteConfig.HeaderValueType.HOST:
                    return context.Host;
                case SiteConfig.HeaderValueType.SCHEME:
                    return context.Memory.Rent(context.Url.Scheme);
                case SiteConfig.HeaderValueType.ADDRESS:
                    return context.Memory.Rent(context.UserHostAddress);
                case SiteConfig.HeaderValueType.PREHOST:
                    int index = context.Host.Span.IndexOf((byte)'.');
                    if (index > 0)
                    {
                        return context.Host.Slice(0, index);
                    }
                    break;
                default:
                    return v.Value;
            }
            return StringValue.Empty;
        }
        public static void WebHeaderConf(NetHttpRequest webR, Span<SiteConfig.HeaderConfItem> HeaderConf, UMC.Net.NetContext context, string account)
        {
            if (HeaderConf.Length > 0)
            {
                var he = HeaderConf.GetEnumerator();
                while (he.MoveNext())
                {
                    var v = WebHeaderConfValue(he.Current, context, account);
                    if (v.IsEmpty == false)
                    {
                        webR.Headers[he.Current.Name] = v;
                    }
                }
            }
        }
        NetHttpRequest WebTransfer(SiteConfig siteConfig, UMC.Net.NetContext context)
        {

            var d = siteConfig.Domains[HttpProxy.WeightUri(siteConfig, context)];
            var dIndex = d.IndexOf('*');
            var url = new Uri(dIndex > 0 ? d.Replace("*", "www") : d);
            var webR = new NetHttpRequest(url, context.PathQuery, context.Memory);
            webR.Proxy = siteConfig.Proxy;
            var Headers = context.Headers.Span;
            foreach (var v in Headers)
            {
                switch (v.HeadKey)
                {
                    case MimeHeadKey.Host:
                    case MimeHeadKey.Connection:
                    case MimeHeadKey.ContentLength:
                        break;
                    default:
                        webR.Add(v);
                        break;

                }
            }

            var host2 = siteConfig.Site.Host;
            if (String.IsNullOrEmpty(host2) == false)
            {
                var port = webR.Address.Port;
                if (String.Equals(host2, "*"))
                {
                    webR.Headers[StringValue.Host] = context.Host;
                }
                else
                {
                    switch (port)
                    {
                        case 80:
                        case 443:
                            break;
                        default:
                            host2 = String.Format("{0}:{1}", host2, port);
                            break;
                    }
                    webR.Headers[StringValue.Host] = context.Memory.Rent(host2);
                }
            }
            WebHeaderConf(webR, siteConfig, context, String.Empty);
            return webR;
        }
        bool ImageCache(NetContext context, string file, string extName, String cacheFile)
        {

            var ckey = context.QueryString.Get("umc-image"u8);
            bool _IsCheckLicense;
            SiteConfig.ImageConfig ImageConfig;
            var contentType = context.Memory.Rent($"image/{extName}");
            if (ckey.IsEmpty && file.Contains("/BgSrc/"))
            {
                _IsCheckLicense = true;
                ImageConfig = new SiteConfig.ImageConfig() { Format = "Optimal" };//new WebMeta().Put("Format", "Optimal");
            }
            else
            {
                _IsCheckLicense = HttpProxy.TryImageConf(ckey, out ImageConfig);
            }
            if (_IsCheckLicense)
            {
                if (String.Equals(ImageConfig.Format, "Src") == false)
                {
                    contentType = context.Memory.Rent($"image/{ImageConfig.Format}");
                }
                var tempFile = System.IO.Path.GetTempFileName();
                var etag = Utility.TimeSpan();
                using (var sWriter = NetClient.MimeStream(tempFile, contentType, etag))
                {
                    using (var fileStream = System.IO.File.OpenRead(file))
                    {
                        SiteImage.Convert(fileStream, sWriter, ImageConfig, cacheFile);
                    }
                    sWriter.Flush();
                    sWriter.Position = 0;
                    context.OutputCache(sWriter, cacheFile);
                    sWriter.Close();
                    Utility.Move(tempFile, cacheFile);
                }
                context.OutputFinish();
                return true;
            }
            return false;
        }
        protected override void TransmitFile(NetContext context, string file, bool isCache)
        {
            var lastIndex = file.LastIndexOf('.');
            if (lastIndex > -1)
            {
                var extName = file.Substring(lastIndex + 1).ToLower();

                switch (extName)
                {
                    case "ico":
                        extName = "x-icon";
                        goto case "png";
                    case "jpg":
                        extName = "jpeg";
                        goto case "png";
                    case "bmp":
                    case "gif":
                    case "webp":
                    case "jpeg":
                    case "png":

                        string cacheFile;
                        var version = String.Empty;
                        if (context.CheckCache("UMC", version, out cacheFile))
                        {
                            context.OutputFinish();
                            return;
                        }
                        if (ImageCache(context, file, extName, cacheFile))
                        {
                            return;
                        }
                        break;
                }
            }

            base.TransmitFile(context, file, isCache);
        }
        void StaticFile(SiteConfig siteConfig, string dir, Net.NetContext context)
        {
            var path = context.Path.Span.UTF8();
            var file = UMC.Data.Utility.FilePath(dir + path);

            switch (context.Method)
            {

                case HttpMethod.GET:

                    var lastIndex = path.LastIndexOf('.');
                    var extName = "html";
                    if (lastIndex > -1 && path.LastIndexOf('/') < lastIndex)
                    {
                        extName = path.Substring(lastIndex + 1).ToLower();

                    }
                    if (System.IO.File.Exists(file))
                    {

                        switch (extName)
                        {
                            case "ico":
                                extName = "x-icon";
                                goto case "png";
                            case "jpg":
                                extName = "jpeg";
                                goto case "png";
                            case "bmp":
                            case "gif":
                            case "webp":
                            case "jpeg":
                            case "png":
                                string filename;
                                if (context.CheckCache(siteConfig.Root, siteConfig.Site.Version, out filename))
                                {
                                    return;
                                }
                                SiteConfig.ImageConfig ImageConfig;
                                var contentType = context.Memory.Rent($"image/{extName}");

                                var ckey = context.QueryString.Get("umc-image"u8);
                                if (ckey.IsEmpty == false)
                                {
                                    if (HttpProxy.TryImageConf(ckey, out ImageConfig) == false)
                                    {
                                        HttpProxy.TryImageConfig(ckey.Span, siteConfig, out ImageConfig);
                                    }
                                    ;

                                }
                                else
                                {
                                    HttpProxy.TryImageConfig(context.Path.Span, contentType.Span, siteConfig, out ImageConfig);

                                }

                                if (ImageConfig != null)
                                {
                                    if (String.Equals(ImageConfig.Format, "Src") == false)
                                    {
                                        contentType = context.Memory.Rent("image/" + ImageConfig.Format);
                                    }
                                    var tempFile = System.IO.Path.GetTempFileName();
                                    var etag = Utility.TimeSpan();
                                    using (var sWriter = NetClient.MimeStream(tempFile, contentType, etag))
                                    {
                                        using (var fileStream = System.IO.File.OpenRead(file))
                                        {
                                            SiteImage.Convert(fileStream, sWriter, ImageConfig, filename);
                                        }
                                        sWriter.Flush();
                                        sWriter.Position = 0;
                                        context.OutputCache(sWriter, filename);
                                        sWriter.Close();
                                    }
                                    Utility.Move(tempFile, filename);
                                    context.OutputFinish();
                                    return;

                                }
                                break;
                        }
                        base.TransmitFile(context, file, true);
                    }
                    else if (dir.EndsWith('/'))
                    {
                        var dirInfo = new System.IO.DirectoryInfo(file);
                        if (dirInfo.Exists)
                        {
                            context.ContentType = StringValue.ContentTypeHtml;// "text/html";
                            using (System.IO.Stream stream = typeof(WebServlet).Assembly
                                                 .GetManifestResourceStream("UMC.ITME.Resources.dir.html"))
                            {
                                stream.CopyTo(context.OutputStream);
                            }
                            var dirs = dirInfo.GetDirectories().OrderBy(r => r.Name);
                            foreach (var d in dirs)
                            {
                                context.Output.WriteLine($"<tr><td><a class=\"icon dir\" href=\"{d.Name}/\">{d.Name}</a></td><td></td><td>{d.LastWriteTimeUtc:G}</td></tr>");
                            }
                            var files = dirInfo.GetFiles().OrderBy(r => r.Name);
                            foreach (var d in files)
                            {
                                context.Output.WriteLine($"<tr><td><a class=\"icon file\" href=\"{d.Name}\">{d.Name}</a></td><td>{Utility.GetBitSize(d.Length)}</td><td>{d.LastWriteTimeUtc:G}</td></tr>");
                            }
                            context.Output.WriteLine("</tbody></table>");
                            context.Output.WriteLine("</body>");
                            context.Output.WriteLine("</html>");
                            context.Output.Flush();
                        }
                        else
                        {
                            NotFound(context, extName, dir);
                        }
                    }
                    else
                    {
                        var staticFile = UMC.Data.Utility.FilePath(file + "/index.html");
                        if (path.IndexOf('.', path.LastIndexOf('/')) == -1 && System.IO.File.Exists(staticFile))
                        {
                            TransmitFile(context, staticFile, true);

                        }
                        else
                        {
                            NotFound(context, extName, dir);
                        }
                    }
                    context.OutputFinish();
                    break;

                // else if (context.Method.Span.SequenceEqual(Mime.PUT))
                case HttpMethod.PUT:

                    if (Data.Utility.CheckSign(context.Headers, "umc-"u8, siteConfig.Site.AppSecret) == false)
                    {

                        var d = UMC.Data.Utility.Writer(file, false);

                        context.ReadAsData((b, i, c) =>
                        {
                            if (c == 0 && b.Length == 0)
                            {

                                d.Close();
                                if (i == 0)
                                {
                                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "文件已经写入"), context.Output);
                                }
                                else
                                {
                                    System.IO.File.Delete(file);

                                    UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "写入文件出错"), context.Output);
                                }
                                context.OutputFinish();
                            }
                            else
                            {
                                d.Write(b, i, c);
                            }
                        });

                    }
                    else
                    {

                        context.StatusCode = 403;
                        UMC.Data.JSON.Serialize(new UMC.Web.WebMeta().Put("msg", "签名不正确"), context.Output);

                        context.OutputFinish();
                    }
                    break;
            }

        }
        static string _MainDomain;
        static StringValue _MainDomainValue;
        static char? _DomainUnion;
        public static char DomainUnion
        {
            get
            {
                if (_DomainUnion.HasValue == false)
                {
                    var s = UMC.Data.WebResource.Instance().Provider["union"] ?? ".";
                    _DomainUnion = s[0];
                }
                return _DomainUnion.Value;
            }
            set
            {
                _DomainUnion = value;
            }
        }
        public static StringValue MainDomainValue
        {
            get
            {
                if (_MainDomainValue == null)
                {

                    _MainDomain = UMC.Data.WebResource.Instance().Provider["domain"] ?? "localhost";
                    _MainDomainValue = new StringValue(_MainDomain);
                }
                return _MainDomainValue;
            }
        }
        public static string MainDomain
        {
            get
            {
                if (String.IsNullOrEmpty(_MainDomain))
                {
                    _MainDomain = UMC.Data.WebResource.Instance().Provider["domain"] ?? "localhost";
                    _MainDomainValue = new StringValue(_MainDomain);
                }
                return _MainDomain;
            }
            set
            {
                _MainDomainValue = new StringValue(value);
                _MainDomain = value;
            }
        }
        void Transfer(SiteConfig siteConfig, UMC.Net.NetContext context, int? StaticPage)
        {

            if (context.IsWebSocket)
            {
                context.Tag = WebTransfer(siteConfig, context);
                return;
            }

            context.UseSynchronousIO();
            if (siteConfig.IsFile)
            {
                this.StaticFile(siteConfig, siteConfig.Domains[0].Substring(7), context);
                return;
            }
            if (context.Method == HttpMethod.GET)
            {
                string cacheFile = String.Empty;
                bool IsCache = false;
                if ((StaticPage ?? HttpProxy.CheckStaticPage(siteConfig, context.Path.Span)) == 0)
                {
                    IsCache = true;
                    if (context.CheckCache(siteConfig.Root, siteConfig.Site.Version, out cacheFile))
                    {
                        context.OutputFinish();
                        return;
                    }
                }

                var wr = WebTransfer(siteConfig, context);
                wr.Net(context, xhr =>
                {
                    StringValue contentType = StringValue.Empty;
                    if (xhr.Headers.TryGet("Content-Type"u8, out var ct))
                    {
                        contentType = ct.Value.Slice(";"u8);
                    }
                    SiteConfig.ImageConfig ImageConfig = null;
                    if (contentType.Contains("image/"u8) && contentType.Contains("svg"u8) == false)
                    {
                        SiteConfig.ImageConfig imageConfig = null;
                        var ckey = context.QueryString.Get("umc-image"u8);
                        if (ckey.IsEmpty == false)
                        {
                            if (HttpProxy.TryImageConf(ckey, out imageConfig) || HttpProxy.TryImageConfig(ckey.Span, siteConfig, out imageConfig))
                            {
                                if (String.Equals(imageConfig.Format, "Src") == false)
                                {
                                    contentType = new StringValue("image/" + imageConfig.Format);
                                }
                                IsCache = true;
                            }
                        }
                        else if (HttpProxy.TryImageConfig(context.Path.Span, contentType.Span, siteConfig, out imageConfig))
                        {
                            if (String.Equals(imageConfig.Format, "Src") == false)
                            {
                                contentType = new StringValue("image/" + imageConfig.Format);
                            }
                            IsCache = true;

                        }
                    }
                    if (xhr.StatusCode == HttpStatusCode.OK && IsCache)
                    {
                        var temp = Path.GetTempFileName();

                        if (ImageConfig != null)
                        {
                            xhr.ReadAsStream(ms =>
                            {
                                var etag = Utility.TimeSpan();
                                var outStream = NetClient.MimeStream(temp, contentType, etag);
                                SiteImage.Convert(ms, outStream, ImageConfig, cacheFile);
                                outStream.Flush();

                                outStream.Position = 0;
                                context.OutputCache(outStream, cacheFile);
                                context.OutputFinish();
                                outStream.Close();
                                Utility.Move(temp, cacheFile);

                            }, err =>
                            {
                                context.Error(xhr.Error);
                            });
                        }
                        else
                        {
                            var tempFile = File.Open(temp, FileMode.Create);

                            var tag = Utility.TimeSpan();
                            context.AddHeader(StringValue.ETag, tag.ToString());

                            if (xhr.ContentLength > -1)
                            {
                                context.ContentLength = xhr.ContentLength;
                            }
                            var ContentEncoding = StringValue.Empty;
                            if (xhr.Headers.TryGet("Content-Encoding"u8, out var contentEncoding))
                            {
                                ContentEncoding = contentEncoding.Value;
                                context.AddHeader(contentEncoding);
                            }
                            context.ContentType = contentType;
                            xhr.ReadAsData(new UMC.Net.DataReader((b, i, c) =>
                            {

                                context.OutputStream.Write(b, i, c);
                                tempFile.Write(b, i, c);

                            }, e =>
                            {
                                context.OutputFinish();
                                tempFile.Flush();
                                tempFile.Position = 0;
                                using (var tem = DataFactory.Instance().Decompress(tempFile, ContentEncoding))
                                {
                                    var t = Path.GetTempFileName();
                                    var cacheStream = NetClient.MimeStream(t, contentType, tag);

                                    tem.CopyTo(cacheStream);
                                    tempFile.Close();
                                    cacheStream.Flush();
                                    cacheStream.Close();

                                    Utility.Move(t, cacheFile);
                                }
                            }, e =>
                            {

                                tempFile.Close();
                                File.Delete(temp);
                                context.Error(e ?? xhr.Error);
                            }).Write);

                        }
                    }
                    else
                    {
                        xhr.Transfer(context);

                    }
                });

            }
            else
            {
                var webR = WebTransfer(siteConfig, context);

                webR.Net(context, xhr =>
                {
                    xhr.Transfer(context);
                });
            }


        }
        void Synchronize(NetContext context)
        {
            context.UseSynchronousIO();

            if (context.ContentLength > 0)
            {
                var buffer = System.Buffers.ArrayPool<byte>.Shared.Rent((int)context.ContentLength.Value);
                var bufferSize = 0;
                context.ReadAsData((b, i, c) =>
                {
                    if (c == 0 && b.Length == 0)
                    {
                        try
                        {
                            if (i == -1)
                            {
                                context.StatusCode = 405;
                                context.ContentType = StringValue.ContentTypeText;
                                context.Output.Write("接收Body错误");
                                context.OutputFinish();
                            }
                            else
                            {
                                var value = System.Text.Encoding.UTF8.GetString(buffer, 0, bufferSize);
                                var p = UMC.Data.WebResource.Instance();
                                String appId = p.Provider["appId"];

                                var hsh = JSON.Deserialize<Hashtable>(value);

                                var time = hsh["time"] as string;
                                var point = hsh["point"] as string;
                                var nvs = new NameValueCollection();

                                nvs.Add("from", appId);
                                nvs.Add("time", time);
                                nvs.Add("point", point);

                                var type = hsh["type"] as string;
                                var secret = p.Provider["appSecret"];
                                if (String.IsNullOrEmpty(type) == false)
                                {

                                    nvs.Add("type", type);

                                    if (String.Equals(hsh["sign"] as string, UMC.Data.Utility.Sign(nvs, secret)))
                                    {
                                        var v = UMC.Data.HotCache.Cache(type, hsh["value"] as Hashtable);
                                        if (v != null)
                                        {
                                            context.ContentType = StringValue.ContentTypeJson;
                                            UMC.Data.JSON.Serialize(v, context.Output, "ts");
                                            return;
                                        }
                                        else
                                        {
                                            context.StatusCode = 404;
                                        }
                                    }
                                    else
                                    {
                                        context.StatusCode = 404;
                                    }
                                }
                                else
                                {

                                    var webMeta = new WebMeta();
                                    if (String.Equals(hsh["sign"] as string, UMC.Data.Utility.Sign(nvs, secret)))
                                    {
                                        if (String.Equals(point, context.Server))
                                        {
                                            context.StatusCode = 405;
                                            webMeta.Put("msg", "不能设置自己为同步节点");
                                        }
                                        else
                                        {
                                            webMeta.Put("msg", "同步节点验证通过").Put("verify", true).Put("server", context.Server);
                                        }
                                    }
                                    else
                                    {
                                        context.StatusCode = 401;
                                        webMeta.Put("msg", "同步节点验证不通过");
                                    }
                                    UMC.Data.JSON.Serialize(webMeta, context.Output);
                                }

                                context.OutputFinish();

                            }
                        }
                        finally
                        {
                            System.Buffers.ArrayPool<byte>.Shared.Return(buffer);

                        }
                    }
                    else
                    {
                        Array.Copy(b, i, buffer, bufferSize, c);
                        bufferSize += c;
                    }
                });

            }
            else
            {
                context.StatusCode = 401;
                context.OutputFinish();
            }

        }
        public static bool IsHttps
        {
            get; set;
        }

        bool IsAllowPath(Span<byte> path, SiteConfig psite)
        {
            var aPath = psite.AllowPath;

            foreach (var d in aPath)
            {
                if (path.StartsWith(d.StartPath.Span) == false)
                {
                    continue;
                }
                if (path.EndsWith(d.EndPath.Span))
                {
                    return true;
                }

            }
            return false;

        }
        SiteConfig CheckSubSite(UMC.Net.NetContext context, SiteConfig psite)
        {
            var rawUrl = context.PathQuery.Span;
            for (var i = 0; i < psite.SubSite.Length; i++)
            {
                var p = psite.SubSite[i];
                if (rawUrl.StartsWith(p.Key.Span))
                {
                    var site2 = UMC.ITME.DataFactory.Instance().SiteConfig(p.Value);
                    if (site2 != null)
                    {
                        psite = site2;
                        if (p.IsDel)
                        {
                            rawUrl = rawUrl.Slice(p.Key.Length);
                            if (rawUrl[0] == '/')
                            {
                                context.RewriteUrl(rawUrl.UTF8());
                            }
                            else
                            {

                                context.RewriteUrl("/" + rawUrl.UTF8());
                            }
                        }
                        break;
                    }
                }
            }
            return psite;
        }
        void Proxy(UMC.Net.NetContext context, SiteConfig psite, StringValue pfxDomain, bool isLoginAPI, int? StaticPage)
        {
            psite = CheckSubSite(context, psite);
            if (psite.IsClose)
            {
                if (psite.Site.Flag < 0 || psite.Domains.Length == 0 || String.IsNullOrEmpty(psite.Site.MarketKey))
                {
                    Close(context);
                }
                else
                {

                    Error(context, $"{psite.Caption}应用已过期", "市场应用已经过期，请联系管理员更新许可证", "");
                }
                return;
            }
            else if (psite.AllowAllPath)
            {
                this.Transfer(psite, context, StaticPage);

                return;
            }
            context.Token = this.AccessToken(context);


            var path = context.Path.Span;//.UTF8();


            if (context.Method == HttpMethod.OPTIONS || IsAllowPath(path, psite))
            {
                if (psite.IsFile)
                {
                    this.StaticFile(psite, psite.Domains[0].Substring(7), context);
                }
                else
                {
                    var httpProxy = new HttpProxy(psite, context, StaticPage ?? HttpProxy.CheckStaticPage(psite, path), pfxDomain);
                    if (context.IsWebSocket)
                    {
                        var webr = httpProxy.Reqesut(context.Transfer(httpProxy.Domain));
                        webr.RawUrl = httpProxy.RawUrl;
                        context.Tag = webr;
                    }
                    else
                    {
                        httpProxy.ProcessRequest();
                    }
                }
                return;

            }
            var IsAuth = false;
            var isBiometric = false;

            var user = context.Token.Identity();
            switch (psite.Site.AuthType ?? Web.WebAuthType.User)
            {
                case Web.WebAuthType.Admin:
                    if (user.IsInRole(UMC.Security.Membership.AdminRole))
                    {
                        IsAuth = true;
                    }
                    else if (UMC.Data.DataFactory.Instance().Roles(user.Id.Value, psite.Site.SiteKey.Value).Contains(UMC.Security.Membership.AdminRole))
                    {
                        IsAuth = true;
                    }
                    break;
                default:
                case Web.WebAuthType.All:
                    IsAuth = true;
                    break;
                case Web.WebAuthType.UserCheck:
                    if (user.IsInRole(UMC.Security.Membership.UserRole))
                    {
                        if (AuthManager.IsAuthorization(user, 0, new StringValue($"Desktop/{psite.Root}"), out isBiometric))
                        {
                            IsAuth = true;
                        }
                    }
                    break;
                case Web.WebAuthType.User:

                    IsAuth = user.IsInRole(UMC.Security.Membership.UserRole);

                    break;
                case Web.WebAuthType.Check:

                    if (user.IsAuthenticated)
                    {
                        if (AuthManager.IsAuthorization(user, 0, new StringValue($"Desktop/{psite.Root}"), out isBiometric))
                        {
                            IsAuth = true;
                        }
                    }
                    else if (psite.IsUnion && context.Cookies.Count == 0)
                    {

                        if (context.Headers.TryGet(StringValue.SecFetchMode.Span, out var mode))
                        {
                            IsAuth = mode.Value.Span.SequenceEqual("cors"u8);
                        }
                    }
                    break;
                case Web.WebAuthType.Guest:
                    if (user.IsAuthenticated)
                    {
                        IsAuth = true;
                    }
                    else if (psite.IsUnion && context.Cookies.Count == 0)
                    {
                        if (context.Headers.TryGet(StringValue.SecFetchMode.Span, out var mode))
                        {
                            IsAuth = mode.Value.Span.SequenceEqual("cors"u8);
                        }
                    }

                    break;
            }



            if (IsAuth)
            {
                var authBiometric = false;
                if (psite.Site.IsAuth == true)
                {
                    var authPath = context.Path.Slice(1);//.Span.UTF8();

                    if (AuthManager.IsAuthorization(user, psite.Site.SiteKey.Value, authPath, out authBiometric) == false)
                    {
                        Error(context, "安全防护", "此资源受保护，请联系应用管理员");
                        return;
                    }

                }
                if (isBiometric || authBiometric)
                {
                    if (context.Token.BiometricTime == 0)
                    {
                        var seesionKey = UMC.Data.Utility.Guid(context.Token.Device.Value);
                        var url = new Uri(AuthDomain(context), $"/Biometric?oauth_callback={Uri.EscapeDataString(context.Url.AbsoluteUri)}&transfer={seesionKey}").AbsoluteUri;
                        context.Redirect(url);
                        return;
                    }
                }

                if (psite.IsFile)
                {
                    this.StaticFile(psite, psite.Domains[0].Substring(7), context);
                    return;
                }
                try
                {
                    var httpProxy = new HttpProxy(psite, context, StaticPage ?? HttpProxy.CheckStaticPage(psite, path), pfxDomain);

                    if (isLoginAPI)
                    {
                        if (user.IsAuthenticated)
                        {
                            httpProxy.LoginRequest();

                        }
                        else
                        {
                            Unauthorized(context);
                        }
                        return;
                    }
                    else if (psite.IsForwardProxy)
                    {
                        Error(context, "正向代理", "此站点为为正向代理不能以反向代理方式访问，请从<a href=\"/UMC.Login\">代理方式进入</a>");
                        return;
                    }
                    if (context.IsWebSocket)
                    {
                        if (psite.Site.UserModel == Entities.UserModel.Bridge)
                        {
                            httpProxy.AuthBridge();
                        }
                        var webr = httpProxy.Reqesut(context.Transfer(httpProxy.Domain));
                        webr.RawUrl = httpProxy.RawUrl;
                        context.Tag = webr;

                    }

                    else
                    {
                        httpProxy.ProcessRequest();
                    }
                }
                catch (WebAbortException)
                {

                }
            }
            else if (user.IsAuthenticated)
            {
                Error(context, "安全审记", "你的权限不足或者登录过期，请 <a href=\"/UMC.Reset\">从新登录</a>");
            }
            else
            {
                Unauthorized(context);
            }

        }
    }

}
